General setup

Setup chunk

Load libraries

knitr::opts_chunk$set(fig.width = 8)
knitr::opts_knit$set(root.dir = normalizePath(".."))
knitr::opts_knit$get("root.dir")
[1] "/nas/groups/treutlein/USERS/tomasgomes/projects/pallium_evo"

Set colours for cell types and regions

meta = read.csv("data/annotations/axolotl_all_umeta.csv", 
                header = T, row.names = 1)
cols_cc = c(
#epen
"#12400c", "#2d6624","#1d4f15", "#174711", "#2d6624", "#3d7f33", "#3b7b30", "#468b3b", "#4f9843","#5dae50", "#66bb58", "#72cd64", "#306a26", "#78d669", "#81e472",
#gaba
"#700209", "#75090e","#7a0f13", "#801517", "#851a1b", "#8a1f1f", "#902423", "#952927", "#9a2d2c","#a03230", "#a53634", "#aa3a39", "#b03f3d","#b54342", "#ba4846", "#c04c4b", "#c5504f", "#ca5554", "#d05959", "#d55e5e","#73050c", "#780c11","#8d2221", "#982b2a","#a23432", "#a83837", "#b2413f", "#b84544", "#bd4a49", "#c85352", #"#cd5756",
#glut
"#054674", "#134d7b","#1d5481", "#265a88", "#2e618e", "#73a4cb", "#366995", "#3e709c", "#4677a2","#4d7ea9", "#5586b0", "#5c8db7", "#6495bd","#6b9cc4", "#7bacd2", "#8ebfe4", "#96c7eb", "#9ecff2", "#18507e", "#18507e","#2a5e8b", "#497ba6","#5889b3", "#6fa0c8","#7fafd6", "#6091ba", "#5182ac", "#3a6c98", "#a6d7f9",
#npc
"#ffb120", "#feb72a","#fdbc34", "#fcc13d", "#fbc745", "#facc4e", "#f9d156", "#f8d65f", "#f8da68","#f7df70", "#f7e479", "#f7e882", "#f7ed8a", "#f7f193", "#eca319"
)
ccnames = unique(sort(meta$cellclusters))
names(cols_cc) = c(ccnames[grepl("epen", ccnames)], ccnames[grepl("GABA", ccnames)],ccnames[grepl("glut", ccnames)],ccnames[grepl("npc", ccnames)])

reg_cols = c("other/unknown_pred" = "#C7CCC7", 
             "medial" = "#52168D", "medial_pred" = "#661CB0", 
             "dorsal" = "#C56007", "dorsal_pred" = "#ED7307", 
             "lateral" = "#118392", "lateral_pred" = "#16A3B6")
reg_cols_simp = c("medial" = "#52168D", "dorsal" = "#C56007", "lateral" = "#118392")

colors <- colorRampPalette(brewer.pal(11,'Spectral')[-6])(100)

Load data

meta = read.csv("data/annotations/axolotl_all_umeta.csv", 
                header = T, row.names = 1)
cols_cc = c(
#epen
"#12400c", "#2d6624","#1d4f15", "#174711", "#2d6624", "#3d7f33", "#3b7b30", "#468b3b", "#4f9843","#5dae50", "#66bb58", "#72cd64", "#306a26", "#78d669", "#81e472",
#gaba
"#700209", "#75090e","#7a0f13", "#801517", "#851a1b", "#8a1f1f", "#902423", "#952927", "#9a2d2c","#a03230", "#a53634", "#aa3a39", "#b03f3d","#b54342", "#ba4846", "#c04c4b", "#c5504f", "#ca5554", "#d05959", "#d55e5e","#73050c", "#780c11","#8d2221", "#982b2a","#a23432", "#a83837", "#b2413f", "#b84544", "#bd4a49", "#c85352", #"#cd5756",
#glut
"#054674", "#134d7b","#1d5481", "#265a88", "#2e618e", "#73a4cb", "#366995", "#3e709c", "#4677a2","#4d7ea9", "#5586b0", "#5c8db7", "#6495bd","#6b9cc4", "#7bacd2", "#8ebfe4", "#96c7eb", "#9ecff2", "#18507e", "#18507e","#2a5e8b", "#497ba6","#5889b3", "#6fa0c8","#7fafd6", "#6091ba", "#5182ac", "#3a6c98", "#a6d7f9",
#npc
"#ffb120", "#feb72a","#fdbc34", "#fcc13d", "#fbc745", "#facc4e", "#f9d156", "#f8d65f", "#f8da68","#f7df70", "#f7e479", "#f7e882", "#f7ed8a", "#f7f193", "#eca319"
)
ccnames = unique(sort(meta$cellclusters))
names(cols_cc) = c(ccnames[grepl("epen", ccnames)], ccnames[grepl("GABA", ccnames)],ccnames[grepl("glut", ccnames)],ccnames[grepl("npc", ccnames)])

reg_cols = c("other/unknown_pred" = "#C7CCC7", 
             "medial" = "#52168D", "medial_pred" = "#661CB0", 
             "dorsal" = "#C56007", "dorsal_pred" = "#ED7307", 
             "lateral" = "#118392", "lateral_pred" = "#16A3B6")
reg_cols_simp = c("medial" = "#52168D", "dorsal" = "#C56007", "lateral" = "#118392")

colors <- colorRampPalette(brewer.pal(11,'Spectral')[-6])(100)

Format metadata

ax_srat = readRDS("data/expression/axolotl_reclust/all_nuclei_clustered_highlevel_anno.RDS")
meta = read.csv("data/annotations/axolotl_all_umeta.csv", 
                header = T, row.names = 1)
ax_srat = AddMetaData(ax_srat, metadata = meta)

div_srat = readRDS("data/expression/axolotl_reclust/Edu_1_2_4_6_8_12_fil_highvarfeat.RDS")

Calculate pseudotime

Process data

Define groups

ax_meta = ax_srat@meta.data[,c("classes", "cellclusters", "regions", "sample", "chem")]
ax_meta$sample = ifelse(endsWith(rownames(ax_meta), "-1_1"), "a1_1",
                 ifelse(endsWith(rownames(ax_meta), "-1_2"), "a1_2",
                 ifelse(endsWith(rownames(ax_meta), "-1_3"), "a3_1",
                 ifelse(endsWith(rownames(ax_meta), "-1_4"), "a3_2", ax_meta$sample))))

meta_regs = read.csv("data/processed/multiome/WP_region_predictions.csv", header = T, row.names = 1)
newcellnames = rownames(meta_regs)
newcellnames = gsub("-a1-1", "-1_1", newcellnames)
newcellnames = gsub("-a1-2", "-1_2", newcellnames)
newcellnames = gsub("-a3-1", "-1_3", newcellnames)
newcellnames = gsub("-a3-2", "-1_4", newcellnames)
rownames(meta_regs) = newcellnames
meta_regs$all_pred_regs_top = paste0(meta_regs$pred_regions_top, "_pred")
ax_meta = merge(ax_meta, meta_regs[,c(2,4)], by = 0, all = T)
ax_meta$pred_regions_top[is.na(ax_meta$pred_regions_top)] = ax_meta$regions[is.na(ax_meta$pred_regions_top)]
ax_meta$all_pred_regs_top[is.na(ax_meta$all_pred_regs_top)] = ax_meta$regions[is.na(ax_meta$all_pred_regs_top)]
rownames(ax_meta) = ax_meta[,1]
ax_meta = ax_meta[,-1]

ax_meta = cbind(ax_meta[rownames(ax_srat@reductions$umap_harmony@cell.embeddings),], 
                ax_srat@reductions$umap_harmony@cell.embeddings)
ax_meta = cbind(unlist(lapply(strsplit(rownames(ax_meta), "-"), function(x) x[1])), ax_meta)
colnames(ax_meta)[1] = "cells"

div_meta = div_srat@meta.data[,c("high_level_anno", "high_level_clustering", "sample", "batch")]
div_meta = cbind(div_meta, div_srat@reductions$umap@cell.embeddings)
div_meta = cbind(unlist(lapply(strsplit(rownames(div_meta), "-"), function(x) x[1])), div_meta)
colnames(div_meta)[1] = "cells"

Subset data

common_cells = c("epen_clus_3", "epen_clus_4")
r_cells = list("hippocampus" = c("glut_SUBSET_0", "glut_SUBSET_4", "glut_SUBSET_5",
                                 "glut_SUBSET_7", "npc_SUBSET_7","npc_SUBSET_11",
                                 "glut_SUBSET_14", "glut_SUBSET_15", "glut_SUBSET_12",
                                 "glut_SUBSET_16", "glut_SUBSET_17", "glut_SUBSET_3",
                                 "npc_SUBSET_1", "npc_SUBSET_3", "npc_SUBSET_9"),
    "lc" = c("glut_SUBSET_9","npc_SUBSET_7","npc_SUBSET_4", "glut_SUBSET_21", "glut_SUBSET_2",
             "glut_SUBSET_25","npc_SUBSET_14","npc_SUBSET_0"),
    "cl111" = c("glut_SUBSET_1", "glut_SUBSET_11", "npc_SUBSET_2","npc_SUBSET_4",
                "npc_SUBSET_7", "npc_SUBSET_13"),
    "eomes" = c("npc_SUBSET_7","npc_SUBSET_4", "glut_SUBSET_10","glut_SUBSET_22"),
    "cl8620_ep" = c("glut_SUBSET_8","glut_SUBSET_6","glut_SUBSET_20", "epen_clus_1",
                    "epen_clus_7", "epen_clus_14"),
    "indirect" = c("npc_SUBSET_7","npc_SUBSET_4", "glut_SUBSET_10","glut_SUBSET_22", 
                   "glut_SUBSET_1", "glut_SUBSET_11", "npc_SUBSET_2","npc_SUBSET_4",
                   "npc_SUBSET_7", "npc_SUBSET_13", "glut_SUBSET_9","npc_SUBSET_7",
                   "npc_SUBSET_4", "glut_SUBSET_21", "glut_SUBSET_2","glut_SUBSET_25",
                   "npc_SUBSET_14","npc_SUBSET_0","glut_SUBSET_0", "glut_SUBSET_4",
                   "glut_SUBSET_5","glut_SUBSET_7", "npc_SUBSET_7","npc_SUBSET_11",
                   "glut_SUBSET_14", "glut_SUBSET_15", "glut_SUBSET_12", "glut_SUBSET_16",
                   "glut_SUBSET_17", "glut_SUBSET_3", "npc_SUBSET_1", "npc_SUBSET_3",
                   "npc_SUBSET_9"))

Get clusters

sub_srat = list()
for(n in names(r_cells)){
  sub_srat[[n]] = ax_srat[,ax_srat$cellclusters %in% c(common_cells, r_cells[[n]])]
}
sub_srat[["all"]] = ax_srat[,ax_srat$cellclusters %in% c(common_cells, unlist(r_cells))]

Load UMAP coord

umap_l = list()
for(n in names(sub_srat)[1:5]){
  n2 = gsub("cl", "", n)
  umap_l[[n]] = read.csv(paste0("results/RNAvelocity/glut_corr/glutNoEp_ss_", n2, "_umap.csv"),
                         header = T, row.names = 1)
}

Slingshot

umap_l = list()
for(n in names(sub_srat)[1:5]){
  n2 = gsub("cl", "", n)
  umap_l[[n]] = read.csv(paste0("results/RNAvelocity/glut_corr/glutNoEp_ss_", n2, "_umap.csv"),
                         header = T, row.names = 1)
}
neolabs = paste0(sub_srat[[n]]@meta.data$cellclusters, "_",
                 sub_srat[[n]]@meta.data$RNA_snn_res.0.6)
lin = getLineages(Reductions(sub_srat[[n]], "umap")@cell.embeddings, 
                  neolabs, start.clus= "epen_clus_3_9", 
                  end.clus = c("glut_SUBSET_7_4", "glut_SUBSET_0_0"))
Error in if (distance > best.stats$distance) { : 
  missing value where TRUE/FALSE needed

n = "lc"
DimPlot(sub_srat[[n]], group.by = "cellclusters", label = T, reduction = "umap")
DimPlot(sub_srat[[n]], group.by = "RNA_snn_res.1.4", label = T, reduction = "umap")


# run slingshot
lin = getLineages(umap_l[[n]], 
                  as.character(sub_srat[[n]]@meta.data$RNA_snn_res.1.4), 
                  start.clus= "2", end.clus = c("3", "15"))
crv = getCurves(lin)

# plotting
pt = apply(crv@assays@data$pseudotime, 1, function(x) min(x[!is.na(x)]))
plotcol <- colors[cut(pt, breaks=100)]
plot(umap_l[[n]], 
     col = plotcol, asp = 1, pch = 16)
lines(SlingshotDataSet(lin), lwd = 3, col = 'black', show.constraints = TRUE)

sce = slingshot(Reductions(sub_srat[[n]], "umap")@cell.embeddings, 
                paste0("cl",as.character(sub_srat[[n]]@meta.data$RNA_snn_res.1)), 
                start.clus= "cl10", end.clus = c("cl0", "cl3"))
UMAPPlot(sub_srat[[n]], group.by = "RNA_snn_res.0.6", label = T)

library(grDevices)
library(RColorBrewer)
colors <- colorRampPalette(brewer.pal(11,'Spectral')[-6])(100)
pt = apply(sce@assays@data$pseudotime, 1, function(x) min(x[!is.na(x)]))
plotcol <- colors[cut(pt, breaks=100)]

plot(Reductions(sub_srat[[n]], "umap")@cell.embeddings, col = plotcol, pch=16, asp = 1)
lines(SlingshotDataSet(sce), lwd=2, col='black')
DimPlot(sub_srat[[n]], group.by = "cellclusters", reduction = "umap")

URD

Prepare URD objects

Calculate DRs

for(n in names(sub_urd)){
  sub_urd[[n]]@var.genes = findVariableGenes(sub_urd[[n]], set.object.var.genes=F,do.plot=F,
                                           diffCV.cutoff=0.3, mean.min=.005, mean.max=100)

  sub_urd[[n]] = calcPCA(sub_urd[[n]], mp.factor = 2)
  pcSDPlot(sub_urd[[n]])
  sub_urd[[n]] = calcTsne(object = sub_urd[[n]])
  plotDim(sub_urd[[n]], "cellclusters")
  
  sub_urd[[n]] = calcDM(sub_urd[[n]], knn = 150, sigma=16)
  
  plotDimArray(sub_urd[[n]], reduction.use = "dm", dims.to.plot = 1:8, 
             outer.title = "Diffusion Map (Sigma 16, 150 NNs): Stage", label="cellclusters",
             plot.title="", legend=F)

  plotDim(sub_urd[[n]], "cellclusters", transitions.plot = 10000)
}

Pseudotime calculation

floods_l = list()
for(n in names(sub_urd)){
  print(n)
  # Here we use all cells from the first stage as the root
  root.cells = cellsInCluster(sub_urd[[n]], clustering = "cellclusters", "epen_clus_3")
  
  # Then we run 'flood' simulations
  floods = floodPseudotime(sub_urd[[n]], root.cells = root.cells, n=250,
                           minimum.cells.flooded = 2, verbose=F)
  # fix excessive NA
  floods = floods[,colSums(!is.na(floods))>1000]
  
  # The we process the simulations into a pseudotime
  sub_urd[[n]] = floodPseudotimeProcess(sub_urd[[n]], floods, floods.name="pseudotime")
  
  pseudotimePlotStabilityOverall(sub_urd[[n]])
  
  plotDists(sub_urd[[n]], "pseudotime", "cellclusters", plot.title="Pseudotime by stage")
  
  floods_l[[n]] = floods
}

Save

end_cc = list("hippocampus" = c("glut_SUBSET_0", "glut_SUBSET_7"),
              "lc" = c("glut_SUBSET_9","glut_SUBSET_2"),
              "cl111" = c("glut_SUBSET_1", "glut_SUBSET_11"),
              "eomes" = c("glut_SUBSET_10","glut_SUBSET_22"),
              "cl8620_ep" = c("glut_SUBSET_8","glut_SUBSET_6"))
subset_dat_l = list()
floods_4_l = list()
for(n in names(sub_urd)){
  print(n)
  # Here we use all cells from the first stage as the root
  root.cells = cellsInCluster(sub_urd[[n]], clustering = "cellclusters", "epen_clus_4")
  
  # Then we run 'flood' simulations
  floods = floodPseudotime(sub_urd[[n]], root.cells = root.cells, n=250,
                           minimum.cells.flooded = 2, verbose=F)
  # fix excessive NA
  floods = floods[,colSums(!is.na(floods))>1000]
  
  # The we process the simulations into a pseudotime
  sub_urd[[n]] = floodPseudotimeProcess(sub_urd[[n]], floods, floods.name="pseudotime_4")
  
  pseudotimePlotStabilityOverall(sub_urd[[n]])
  
  plotDists(sub_urd[[n]], "pseudotime_4", "cellclusters", plot.title="Pseudotime by stage")
  
  # Create a subsetted object of just those cells from the final stage
  subsetdat = urdSubset(sub_urd[[n]], 
                        cells.keep=cellsInCluster(sub_urd[[n]], "cellclusters", end_cc[[n]]))
  subset_dat_l[[n]] = subsetdat
  floods_4_l[[n]] = floods
}
[1] "hippocampus"
[1] "lc"
[1] "cl111"
[1] "eomes"
[1] "cl8620_ep"

Determine new identities for end tips

save(subset_dat_l, floods_l, sub_urd, floods_4_l, file = "data/processed/URD/URD_lists.RData")
axials_l = list()
for(n in names(sub_urd)){
  # Use the variable genes that were calculated only on the final group of stages (which
  # contain the last stage).
  subset_dat_l[[n]]@var.genes = sub_urd[[n]]@var.genes[sub_urd[[n]]@var.genes %in% rownames(subset_dat_l[[n]]@logupx.data)]
  
  # Calculate PCA and tSNE
  subset_dat_l[[n]] = calcPCA(subset_dat_l[[n]], mp.factor = 1.5)
  pcSDPlot(subset_dat_l[[n]])
  
  set.seed(20)
  subsetdat = calcTsne(subset_dat_l[[n]])
  
  # Calculate graph clustering of these cells
  subset_dat_l[[n]] = graphClustering(subset_dat_l[[n]], 
                                      num.nn = 50, do.jaccard=T, method="Louvain")
  plotDim(subset_dat_l[[n]], "Louvain-50", 
          plot.title = "Louvain (50 NN) graph clustering", point.size=3)
  plotDim(subset_dat_l[[n]], "cellclusters", plot.title = "cellclusters", point.size=3)
  
  
  # Copy cluster identities from axial.6somite object to a new clustering ("tip.clusters") in the full axial object.
  sub_urd[[n]]@group.ids[rownames(subset_dat_l[[n]]@group.ids), "tip.clusters"] = subset_dat_l[[n]]@group.ids$`Louvain-50`
  
  # Determine the parameters of the logistic used to bias the transition probabilities. 
  # The procedure is relatively robust to this parameter, but the cell numbers may need to be
  # modified for larger or smaller data sets.
  axial.ptlogistic = pseudotimeDetermineLogistic(sub_urd[[n]], "pseudotime",
                                                 optimal.cells.forward=20, max.cells.back=20,
                                                 do.plot = T)
  
  axial.biased.tm = as.matrix(pseudotimeWeightTransitionMatrix(sub_urd[[n]], "pseudotime",
                                                               logistic.params=axial.ptlogistic))
  
  axials_l[[n]] = list("log" = axial.ptlogistic, "tm" = axial.biased.tm)
}
[1] "2022-05-24 19:04:04: Centering and scaling data."
[1] "2022-05-24 19:04:07: Removing genes with no variation."
[1] "2022-05-24 19:04:08: Calculating PCA."
[1] "2022-05-24 19:09:24: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 3.85101341791152"
[1] "11 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 22 PCs."
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00114130123513062"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00114130123513062"
[1] "2022-05-24 19:10:19: Centering and scaling data."
[1] "2022-05-24 19:10:24: Removing genes with no variation."
[1] "2022-05-24 19:10:25: Calculating PCA."
[1] "2022-05-24 19:15:31: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 4.99020501012741"
[1] "16 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 32 PCs."

Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00246109710635848"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00246109710635848"
[1] "2022-05-24 19:15:53: Centering and scaling data."
[1] "2022-05-24 19:15:57: Removing genes with no variation."
[1] "2022-05-24 19:15:58: Calculating PCA."
[1] "2022-05-24 19:20:34: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 4.8946219896864"
[1] "9 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 18 PCs."

Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00277521046018166"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00277521046018166"
[1] "2022-05-24 19:20:55: Centering and scaling data."
[1] "2022-05-24 19:20:57: Removing genes with no variation."
[1] "2022-05-24 19:20:58: Calculating PCA."
[1] "2022-05-24 19:21:51: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 8.02811103081175"
[1] "10 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 20 PCs."

Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.0052763887436612"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.0052763887436612"
[1] "2022-05-24 19:21:59: Centering and scaling data."
[1] "2022-05-24 19:22:03: Removing genes with no variation."
[1] "2022-05-24 19:22:03: Calculating PCA."
[1] "2022-05-24 19:25:52: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 4.55584963964571"
[1] "12 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 24 PCs."

Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00446986465888735"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00446986465888735"

Random walk simulations

axials_4_l = list()
for(n in names(sub_urd)){
  # Use the variable genes that were calculated only on the final group of stages (which
  # contain the last stage).
  subset_dat_l[[n]]@var.genes = sub_urd[[n]]@var.genes[sub_urd[[n]]@var.genes %in% rownames(subset_dat_l[[n]]@logupx.data)]
  
  # Calculate PCA and tSNE
  subset_dat_l[[n]] = calcPCA(subset_dat_l[[n]], mp.factor = 1.5)
  pcSDPlot(subset_dat_l[[n]])
  
  set.seed(20)
  subsetdat = calcTsne(subset_dat_l[[n]])
  
  # Calculate graph clustering of these cells
  subset_dat_l[[n]] = graphClustering(subset_dat_l[[n]], 
                                      num.nn = 50, do.jaccard=T, method="Louvain")
  plotDim(subset_dat_l[[n]], "Louvain-50", 
          plot.title = "Louvain (50 NN) graph clustering", point.size=3)
  plotDim(subset_dat_l[[n]], "cellclusters", plot.title = "cellclusters", point.size=3)
  
  
  # Copy cluster identities from axial.6somite object to a new clustering ("tip.clusters") in the full axial object.
  sub_urd[[n]]@group.ids[rownames(subset_dat_l[[n]]@group.ids), "tip.clusters"] = subset_dat_l[[n]]@group.ids$`Louvain-50`
  
  # Determine the parameters of the logistic used to bias the transition probabilities. 
  # The procedure is relatively robust to this parameter, but the cell numbers may need to be
  # modified for larger or smaller data sets.
  axial.ptlogistic = pseudotimeDetermineLogistic(sub_urd[[n]], "pseudotime_4",
                                                 optimal.cells.forward=20, max.cells.back=20,
                                                 do.plot = T)
  
  axial.biased.tm = as.matrix(pseudotimeWeightTransitionMatrix(sub_urd[[n]], "pseudotime_4",
                                                               logistic.params=axial.ptlogistic))
  
  axials_4_l[[n]] = list("log" = axial.ptlogistic, "tm" = axial.biased.tm)
}
[1] "2022-05-25 00:21:49: Centering and scaling data."
[1] "2022-05-25 00:21:52: Removing genes with no variation."
[1] "2022-05-25 00:21:53: Calculating PCA."
[1] "2022-05-25 00:26:13: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 3.85101341791152"
[1] "11 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 22 PCs."
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00113280913779219"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00113280913779219"
[1] "2022-05-25 00:27:07: Centering and scaling data."
[1] "2022-05-25 00:27:11: Removing genes with no variation."
[1] "2022-05-25 00:27:12: Calculating PCA."
[1] "2022-05-25 00:33:12: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 4.99020501012741"
[1] "16 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 32 PCs."

Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00265125322040561"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00265125322040561"
[1] "2022-05-25 00:33:34: Centering and scaling data."
[1] "2022-05-25 00:33:38: Removing genes with no variation."
[1] "2022-05-25 00:33:38: Calculating PCA."
[1] "2022-05-25 00:38:07: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 4.8946219896864"
[1] "9 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 18 PCs."

Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00298964955430957"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00298964955430957"
[1] "2022-05-25 00:38:26: Centering and scaling data."
[1] "2022-05-25 00:38:28: Removing genes with no variation."
[1] "2022-05-25 00:38:28: Calculating PCA."
[1] "2022-05-25 00:39:17: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 8.02811103081175"
[1] "10 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 20 PCs."

Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00562420847777713"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00562420847777713"
[1] "2022-05-25 00:39:26: Centering and scaling data."
[1] "2022-05-25 00:39:28: Removing genes with no variation."
[1] "2022-05-25 00:39:29: Calculating PCA."
[1] "2022-05-25 00:43:04: Estimating significant PCs."
[1] "Marchenko-Pastur eigenvalue null upper bound: 4.55584963964571"
[1] "12 PCs have eigenvalues larger than 1.5 times null upper bound."
[1] "Storing 24 PCs."

Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

[1] "Mean pseudotime back (~20 cells) 0.00469503083919989"
[1] "Chance of accepted move to equal pseudotime is 0.5"
[1] "Mean pseudotime forward (~20 cells) -0.00469503083919989"

subfix_l = list()
for(n in names(sub_urd)){
  subsetfix = urdSubset(sub_urd[[n]], 
                        cells.keep=rownames(sub_urd[[n]]@meta)[rownames(sub_urd[[n]]@meta)%in%rownames(axials_l[[n]][["tm"]])])
  root.cells = cellsInCluster(sub_urd[[n]], clustering = "cellclusters", "epen_clus_3")
  
  # Simulate the biased random walks from each tip
  axial.walks = simulateRandomWalksFromTips(subsetfix, tip.group.id="tip.clusters",
                                            root.cells=root.cells, n.per.tip = 25000,
                                            transition.matrix = axials_l[[n]][["tm"]], 
                                            root.visits = 1, max.steps = 10000, verbose = F)
  
  # Process the biased random walks into visitation frequencies
  subsetfix = processRandomWalksFromTips(subsetfix, axial.walks, verbose = F)
  
  axials_l[[n]][["walks"]] = axial.walks
  subfix_l[[n]] = subsetfix
}

Save

subfix_4_l = list()
for(n in names(sub_urd)){
  subsetfix = urdSubset(sub_urd[[n]], 
                        cells.keep=rownames(sub_urd[[n]]@meta)[rownames(sub_urd[[n]]@meta)%in%rownames(axials_4_l[[n]][["tm"]])])
  root.cells = cellsInCluster(sub_urd[[n]], clustering = "cellclusters", "epen_clus_4")
  
  # Simulate the biased random walks from each tip
  axial.walks = simulateRandomWalksFromTips(subsetfix, tip.group.id="tip.clusters",
                                            root.cells=root.cells, n.per.tip = 25000,
                                            transition.matrix = axials_4_l[[n]][["tm"]], 
                                            root.visits = 1, max.steps = 10000, verbose = F)
  
  # Process the biased random walks into visitation frequencies
  subsetfix = processRandomWalksFromTips(subsetfix, axial.walks, verbose = F)
  
  axials_4_l[[n]][["walks"]] = axial.walks
  subfix_4_l[[n]] = subsetfix
}

Check tip clusters

save(subset_dat_l, floods_l, floods_4_l, sub_urd, axials_l, subfix_l, axials_4_l, subfix_4_l,
     file = "data/processed/URD/URD_lists.RData")

Plot trees

max_vals = list()
for(n in names(subfix_4_l)){
  plt1 = plotDim(subfix_4_l[[n]], "cellclusters", plot.title=n)
  plt2 = plotDim(subfix_4_l[[n]], "tip.clusters", plot.title=n)
  print(plt1+plt2)
  
  max_vals[[n]] = sort(tapply(subfix_4_l[[n]]@pseudotime$pseudotime_4,
                    subfix_4_l[[n]]@group.ids$tip.clusters, max))
}
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

Save trees

for(n in names(tree_plt_l)){
  pdf(paste0("results/pseudotime/SS_", n, "_tree.pdf"), height = 3.5, width = 4)
  print(tree_plt_l[[n]])
  dev.off()
}

Load RNA velocity pseudotime

for(n in names(tree_plt_l)){
  pdf(paste0("results/pseudotime/SS_", n, "_tree.pdf"), height = 3.5, width = 4)
  print(tree_plt_l[[n]])
  dev.off()
}

Plot pseudotime comparisons

glut_dat_df = read.csv("results/RNAvelocity/glut_corr/heatmap_gen/glut_dat_df.csv", 
                       header = T, row.names = 1)
newcellnames = rownames(glut_dat_df)
newcellnames = gsub("_D1", "-1_1_5", newcellnames)
newcellnames = gsub("_D2", "-1_2_5", newcellnames)
newcellnames = gsub("_L1", "-1_3_5", newcellnames)
newcellnames = gsub("_L2", "-1_4_5", newcellnames)
newcellnames = gsub("_M1", "-1_5_5", newcellnames)
newcellnames = gsub("_M2", "-1_6_5", newcellnames)
newcellnames = gsub("_a1_1", "-1_1", newcellnames)
newcellnames = gsub("_a1_2", "-1_2", newcellnames)
newcellnames = gsub("_a3_1", "-1_3", newcellnames)
newcellnames = gsub("_a3_2", "-1_4", newcellnames)
rownames(glut_dat_df) = newcellnames

Save comparisons

plt_comp_l = list()
for(n in names(subfix_l)){
  plot_df = merge(glut_dat_df, subfix_4_l[[n]]@pseudotime, by = 0)
  plot_df$subclasses = subfix_4_l[[n]]@meta[plot_df$Row.names,"subclasses"]
  plot_df$subclasses = factor(plot_df$subclasses, 
                              levels = c("Ependymal", "NPC", "Glutamatergic"))
  cpe = round(cor(plot_df$newpt, plot_df$pseudotime_4, method = "pe"), 2)
  
  plt_comp_l[[n]] = ggplot(plot_df, aes(x = newpt, y = pseudotime_4, 
                                        colour = cellclusters, size = subclasses))+
    geom_point()+
    scale_colour_manual(values = cols_cc, limits = force)+
    scale_size_manual(values = c(0.5, 0.95, 1.35))+
    labs(title = n, subtitle = paste0("PCC = ", cpe), 
         x = "Pseudotime (scVelo)", y = "Pseudotime (URD)")+
    guides(colour = guide_legend(), size = guide_legend())+
    theme_classic()+
    theme(aspect.ratio = 1,
          axis.text = element_text(colour = "black", size = 6),
          axis.title = element_text(size = 7),
          plot.title = element_text(size = 8),
          plot.subtitle = element_text(size = 7),
          legend.text = element_text(size = 6),
          legend.title = element_text(size = 7),
          legend.key.size = unit(0.35, "cm"))
}

Make proportion plots

for(n in names(plt_comp_l)){
  pdf(paste0("results/pseudotime/SS_", n, "_comp.pdf"), height = 4, width = 4.5)
  print(plt_comp_l[[n]])
  dev.off()
}

Save proportions

smo_prop_list = list()
for(n in names(subfix_4_l)){
  # subset data
  submeta = data.frame("pseudotime" = subfix_4_l[[n]]@pseudotime$pseudotime_4,
                       "cellclusters" = subfix_4_l[[n]]@meta$cellclusters)
  
  med_pt_cc = sort(tapply(submeta$pseudotime, submeta$cellclusters, median))
  
  lt_bins = cut(submeta$pseudotime, 100) # 100 equally-sized bins
  plot_df = data.frame(bins = lt_bins, 
                       cst = as.character(submeta$cellclusters))
  tab_df = table(plot_df$bins, plot_df$cst)
  
  # remove cell types that are too rare (<5%)
  tab_df = reshape2::melt(tab_df/rowSums(tab_df))
  usecl = tapply(tab_df$value, tab_df$Var2, function(x) any(x>0.05))
  plot_df = plot_df[plot_df$cst %in% names(usecl)[usecl],]
  tab_df = table(plot_df$bins,plot_df$cst)
  
  # normalise by cell type abundance
  prop_w = prop.table(table(plot_df$cst))
  tab_df = t(apply(tab_df, 1, function(x) x/prop_w[colnames(tab_df)]))
  
  # reshape
  tab_df = reshape2::melt(tab_df/rowSums(tab_df))
  tab_df$Var2 = as.character(tab_df$Var2)
  
  # prevent discontinuity by copying the previous column (likely not happening)
  tab_df = tab_df[order(tab_df$Var1, decreasing = F),]
  for(i in unique(tab_df$Var1)){
    if(any(is.nan(tab_df$value[tab_df$Var1==i]))){
      tab_df$value[tab_df$Var1==i] = prev
    }
    prev = tab_df$value[tab_df$Var1==i]
  }
  
  # smoothen the proportions (and force constrain to 0-1)
  tab_df2 = tab_df
  tab_df2$value2 = tab_df2$value
  for(i in unique(tab_df2$Var2)){
    fff = loess(value~as.numeric(Var1), data = tab_df2[tab_df2$Var2==i,], 
                span = 0.5)
    pred = predict(fff)
    pred[pred>1] = 1
    pred[pred<0] = 0
    tab_df2$value2[tab_df2$Var2==i] = pred
  }
  
  # force constrain each interval to 0-1 by doing proportion
  for(i in unique(tab_df2$Var1)){
    tab_df2$value2[tab_df2$Var1==i] = tab_df2$value2[tab_df2$Var1==i]/sum(tab_df2$value2[tab_df2$Var1==i])
  }
  
  tab_df2$major = unlist(lapply(strsplit(tab_df2$Var2, "_"), function(x) x[1]))
  res = list()
  for(nnn in unique(tab_df2$Var1)){
    ss=tapply(tab_df2[tab_df2$Var1==nnn,"value2"], tab_df2[tab_df2$Var1==nnn,"major"], sum)
    res[[nnn]] = which.max(ss)
  }

  smo_prop_list[[n]] = ggplot(tab_df2, aes(x = Var1, y = value2, group = Var2, fill = Var2))+
    geom_area()+
    scale_y_continuous(expand = c(0,0))+
    scale_fill_manual(values = cols_cc[names(cols_cc) %in% tab_df2$Var2])+
    labs(x = "Bins", y = "Proportion", fill = "Cell type")+
    theme_classic()+
    theme(axis.text.x = element_blank(),
          axis.text.y = element_text(size = 6.5, colour = "black"),
          axis.ticks.x = element_blank(),
          axis.line = element_blank(),
          axis.title = element_text(size = 7),
          legend.text = element_text(size = 6),
          legend.title = element_text(size = 7),
          legend.key.size = unit(0.4, "cm"))
}
LS0tCnRpdGxlOiAiUHNldWRvdGltZSBmb3Igc3RlYWR5LXN0YXRlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKCiMgR2VuZXJhbCBzZXR1cApTZXR1cCBjaHVuawoKYGBge3IsIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gOCkKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBub3JtYWxpemVQYXRoKCIuLiIpKQprbml0cjo6b3B0c19rbml0JGdldCgicm9vdC5kaXIiKQpgYGAKCkxvYWQgbGlicmFyaWVzCgpgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoaGFybW9ueSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShtZ2N2KQpsaWJyYXJ5KGZvcmVhY2gpCmxpYnJhcnkoZG9QYXJhbGxlbCkKbGlicmFyeShwYXJhbGxlbCkKbGlicmFyeShVUkQpCmxpYnJhcnkoc2xpbmdzaG90KQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgpTZXQgY29sb3VycyBmb3IgY2VsbCB0eXBlcyBhbmQgcmVnaW9ucwoKYGBge3J9Cm1ldGEgPSByZWFkLmNzdigiZGF0YS9hbm5vdGF0aW9ucy9heG9sb3RsX2FsbF91bWV0YS5jc3YiLCAKICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsIHJvdy5uYW1lcyA9IDEpCmNvbHNfY2MgPSBjKAojZXBlbgoiIzEyNDAwYyIsICIjMmQ2NjI0IiwiIzFkNGYxNSIsICIjMTc0NzExIiwgIiMyZDY2MjQiLCAiIzNkN2YzMyIsICIjM2I3YjMwIiwgIiM0NjhiM2IiLCAiIzRmOTg0MyIsIiM1ZGFlNTAiLCAiIzY2YmI1OCIsICIjNzJjZDY0IiwgIiMzMDZhMjYiLCAiIzc4ZDY2OSIsICIjODFlNDcyIiwKI2dhYmEKIiM3MDAyMDkiLCAiIzc1MDkwZSIsIiM3YTBmMTMiLCAiIzgwMTUxNyIsICIjODUxYTFiIiwgIiM4YTFmMWYiLCAiIzkwMjQyMyIsICIjOTUyOTI3IiwgIiM5YTJkMmMiLCIjYTAzMjMwIiwgIiNhNTM2MzQiLCAiI2FhM2EzOSIsICIjYjAzZjNkIiwiI2I1NDM0MiIsICIjYmE0ODQ2IiwgIiNjMDRjNGIiLCAiI2M1NTA0ZiIsICIjY2E1NTU0IiwgIiNkMDU5NTkiLCAiI2Q1NWU1ZSIsIiM3MzA1MGMiLCAiIzc4MGMxMSIsIiM4ZDIyMjEiLCAiIzk4MmIyYSIsIiNhMjM0MzIiLCAiI2E4MzgzNyIsICIjYjI0MTNmIiwgIiNiODQ1NDQiLCAiI2JkNGE0OSIsICIjYzg1MzUyIiwgIyIjY2Q1NzU2IiwKI2dsdXQKIiMwNTQ2NzQiLCAiIzEzNGQ3YiIsIiMxZDU0ODEiLCAiIzI2NWE4OCIsICIjMmU2MThlIiwgIiM3M2E0Y2IiLCAiIzM2Njk5NSIsICIjM2U3MDljIiwgIiM0Njc3YTIiLCIjNGQ3ZWE5IiwgIiM1NTg2YjAiLCAiIzVjOGRiNyIsICIjNjQ5NWJkIiwiIzZiOWNjNCIsICIjN2JhY2QyIiwgIiM4ZWJmZTQiLCAiIzk2YzdlYiIsICIjOWVjZmYyIiwgIiMxODUwN2UiLCAiIzE4NTA3ZSIsIiMyYTVlOGIiLCAiIzQ5N2JhNiIsIiM1ODg5YjMiLCAiIzZmYTBjOCIsIiM3ZmFmZDYiLCAiIzYwOTFiYSIsICIjNTE4MmFjIiwgIiMzYTZjOTgiLCAiI2E2ZDdmOSIsCiNucGMKIiNmZmIxMjAiLCAiI2ZlYjcyYSIsIiNmZGJjMzQiLCAiI2ZjYzEzZCIsICIjZmJjNzQ1IiwgIiNmYWNjNGUiLCAiI2Y5ZDE1NiIsICIjZjhkNjVmIiwgIiNmOGRhNjgiLCIjZjdkZjcwIiwgIiNmN2U0NzkiLCAiI2Y3ZTg4MiIsICIjZjdlZDhhIiwgIiNmN2YxOTMiLCAiI2VjYTMxOSIKKQpjY25hbWVzID0gdW5pcXVlKHNvcnQobWV0YSRjZWxsY2x1c3RlcnMpKQpuYW1lcyhjb2xzX2NjKSA9IGMoY2NuYW1lc1tncmVwbCgiZXBlbiIsIGNjbmFtZXMpXSwgY2NuYW1lc1tncmVwbCgiR0FCQSIsIGNjbmFtZXMpXSxjY25hbWVzW2dyZXBsKCJnbHV0IiwgY2NuYW1lcyldLGNjbmFtZXNbZ3JlcGwoIm5wYyIsIGNjbmFtZXMpXSkKCnJlZ19jb2xzID0gYygib3RoZXIvdW5rbm93bl9wcmVkIiA9ICIjQzdDQ0M3IiwgCiAgICAgICAgICAgICAibWVkaWFsIiA9ICIjNTIxNjhEIiwgIm1lZGlhbF9wcmVkIiA9ICIjNjYxQ0IwIiwgCiAgICAgICAgICAgICAiZG9yc2FsIiA9ICIjQzU2MDA3IiwgImRvcnNhbF9wcmVkIiA9ICIjRUQ3MzA3IiwgCiAgICAgICAgICAgICAibGF0ZXJhbCIgPSAiIzExODM5MiIsICJsYXRlcmFsX3ByZWQiID0gIiMxNkEzQjYiKQpyZWdfY29sc19zaW1wID0gYygibWVkaWFsIiA9ICIjNTIxNjhEIiwgImRvcnNhbCIgPSAiI0M1NjAwNyIsICJsYXRlcmFsIiA9ICIjMTE4MzkyIikKCmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoMTEsJ1NwZWN0cmFsJylbLTZdKSgxMDApCmBgYAoKCgpMb2FkIGRhdGEKCmBgYHtyfQpheF9zcmF0ID0gcmVhZFJEUygiZGF0YS9leHByZXNzaW9uL2F4b2xvdGxfcmVjbHVzdC9hbGxfbnVjbGVpX2NsdXN0ZXJlZF9oaWdobGV2ZWxfYW5uby5SRFMiKQptZXRhID0gcmVhZC5jc3YoImRhdGEvYW5ub3RhdGlvbnMvYXhvbG90bF9hbGxfdW1ldGEuY3N2IiwgCiAgICAgICAgICAgICAgICBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxKQpheF9zcmF0ID0gQWRkTWV0YURhdGEoYXhfc3JhdCwgbWV0YWRhdGEgPSBtZXRhKQoKZGl2X3NyYXQgPSByZWFkUkRTKCJkYXRhL2V4cHJlc3Npb24vYXhvbG90bF9yZWNsdXN0L0VkdV8xXzJfNF82XzhfMTJfZmlsX2hpZ2h2YXJmZWF0LlJEUyIpCmBgYAoKRm9ybWF0IG1ldGFkYXRhCgpgYGB7cn0KYXhfbWV0YSA9IGF4X3NyYXRAbWV0YS5kYXRhWyxjKCJjbGFzc2VzIiwgImNlbGxjbHVzdGVycyIsICJyZWdpb25zIiwgInNhbXBsZSIsICJjaGVtIildCmF4X21ldGEkc2FtcGxlID0gaWZlbHNlKGVuZHNXaXRoKHJvd25hbWVzKGF4X21ldGEpLCAiLTFfMSIpLCAiYTFfMSIsCiAgICAgICAgICAgICAgICAgaWZlbHNlKGVuZHNXaXRoKHJvd25hbWVzKGF4X21ldGEpLCAiLTFfMiIpLCAiYTFfMiIsCiAgICAgICAgICAgICAgICAgaWZlbHNlKGVuZHNXaXRoKHJvd25hbWVzKGF4X21ldGEpLCAiLTFfMyIpLCAiYTNfMSIsCiAgICAgICAgICAgICAgICAgaWZlbHNlKGVuZHNXaXRoKHJvd25hbWVzKGF4X21ldGEpLCAiLTFfNCIpLCAiYTNfMiIsIGF4X21ldGEkc2FtcGxlKSkpKQoKbWV0YV9yZWdzID0gcmVhZC5jc3YoImRhdGEvcHJvY2Vzc2VkL211bHRpb21lL1dQX3JlZ2lvbl9wcmVkaWN0aW9ucy5jc3YiLCBoZWFkZXIgPSBULCByb3cubmFtZXMgPSAxKQpuZXdjZWxsbmFtZXMgPSByb3duYW1lcyhtZXRhX3JlZ3MpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIi1hMS0xIiwgIi0xXzEiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIi1hMS0yIiwgIi0xXzIiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIi1hMy0xIiwgIi0xXzMiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIi1hMy0yIiwgIi0xXzQiLCBuZXdjZWxsbmFtZXMpCnJvd25hbWVzKG1ldGFfcmVncykgPSBuZXdjZWxsbmFtZXMKbWV0YV9yZWdzJGFsbF9wcmVkX3JlZ3NfdG9wID0gcGFzdGUwKG1ldGFfcmVncyRwcmVkX3JlZ2lvbnNfdG9wLCAiX3ByZWQiKQpheF9tZXRhID0gbWVyZ2UoYXhfbWV0YSwgbWV0YV9yZWdzWyxjKDIsNCldLCBieSA9IDAsIGFsbCA9IFQpCmF4X21ldGEkcHJlZF9yZWdpb25zX3RvcFtpcy5uYShheF9tZXRhJHByZWRfcmVnaW9uc190b3ApXSA9IGF4X21ldGEkcmVnaW9uc1tpcy5uYShheF9tZXRhJHByZWRfcmVnaW9uc190b3ApXQpheF9tZXRhJGFsbF9wcmVkX3JlZ3NfdG9wW2lzLm5hKGF4X21ldGEkYWxsX3ByZWRfcmVnc190b3ApXSA9IGF4X21ldGEkcmVnaW9uc1tpcy5uYShheF9tZXRhJGFsbF9wcmVkX3JlZ3NfdG9wKV0Kcm93bmFtZXMoYXhfbWV0YSkgPSBheF9tZXRhWywxXQpheF9tZXRhID0gYXhfbWV0YVssLTFdCgpheF9tZXRhID0gY2JpbmQoYXhfbWV0YVtyb3duYW1lcyhheF9zcmF0QHJlZHVjdGlvbnMkdW1hcF9oYXJtb255QGNlbGwuZW1iZWRkaW5ncyksXSwgCiAgICAgICAgICAgICAgICBheF9zcmF0QHJlZHVjdGlvbnMkdW1hcF9oYXJtb255QGNlbGwuZW1iZWRkaW5ncykKYXhfbWV0YSA9IGNiaW5kKHVubGlzdChsYXBwbHkoc3Ryc3BsaXQocm93bmFtZXMoYXhfbWV0YSksICItIiksIGZ1bmN0aW9uKHgpIHhbMV0pKSwgYXhfbWV0YSkKY29sbmFtZXMoYXhfbWV0YSlbMV0gPSAiY2VsbHMiCgpkaXZfbWV0YSA9IGRpdl9zcmF0QG1ldGEuZGF0YVssYygiaGlnaF9sZXZlbF9hbm5vIiwgImhpZ2hfbGV2ZWxfY2x1c3RlcmluZyIsICJzYW1wbGUiLCAiYmF0Y2giKV0KZGl2X21ldGEgPSBjYmluZChkaXZfbWV0YSwgZGl2X3NyYXRAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5ncykKZGl2X21ldGEgPSBjYmluZCh1bmxpc3QobGFwcGx5KHN0cnNwbGl0KHJvd25hbWVzKGRpdl9tZXRhKSwgIi0iKSwgZnVuY3Rpb24oeCkgeFsxXSkpLCBkaXZfbWV0YSkKY29sbmFtZXMoZGl2X21ldGEpWzFdID0gImNlbGxzIgpgYGAKCgoKIyBDYWxjdWxhdGUgcHNldWRvdGltZQojIyBQcm9jZXNzIGRhdGEKRGVmaW5lIGdyb3VwcwoKYGBge3J9CmNvbW1vbl9jZWxscyA9IGMoImVwZW5fY2x1c18zIiwgImVwZW5fY2x1c180IikKcl9jZWxscyA9IGxpc3QoImhpcHBvY2FtcHVzIiA9IGMoImdsdXRfU1VCU0VUXzAiLCAiZ2x1dF9TVUJTRVRfNCIsICJnbHV0X1NVQlNFVF81IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdsdXRfU1VCU0VUXzciLCAibnBjX1NVQlNFVF83IiwibnBjX1NVQlNFVF8xMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnbHV0X1NVQlNFVF8xNCIsICJnbHV0X1NVQlNFVF8xNSIsICJnbHV0X1NVQlNFVF8xMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnbHV0X1NVQlNFVF8xNiIsICJnbHV0X1NVQlNFVF8xNyIsICJnbHV0X1NVQlNFVF8zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5wY19TVUJTRVRfMSIsICJucGNfU1VCU0VUXzMiLCAibnBjX1NVQlNFVF85IiksCiAgICAibGMiID0gYygiZ2x1dF9TVUJTRVRfOSIsIm5wY19TVUJTRVRfNyIsIm5wY19TVUJTRVRfNCIsICJnbHV0X1NVQlNFVF8yMSIsICJnbHV0X1NVQlNFVF8yIiwKICAgICAgICAgICAgICJnbHV0X1NVQlNFVF8yNSIsIm5wY19TVUJTRVRfMTQiLCJucGNfU1VCU0VUXzAiKSwKICAgICJjbDExMSIgPSBjKCJnbHV0X1NVQlNFVF8xIiwgImdsdXRfU1VCU0VUXzExIiwgIm5wY19TVUJTRVRfMiIsIm5wY19TVUJTRVRfNCIsCiAgICAgICAgICAgICAgICAibnBjX1NVQlNFVF83IiwgIm5wY19TVUJTRVRfMTMiKSwKICAgICJlb21lcyIgPSBjKCJucGNfU1VCU0VUXzciLCJucGNfU1VCU0VUXzQiLCAiZ2x1dF9TVUJTRVRfMTAiLCJnbHV0X1NVQlNFVF8yMiIpLAogICAgImNsODYyMF9lcCIgPSBjKCJnbHV0X1NVQlNFVF84IiwiZ2x1dF9TVUJTRVRfNiIsImdsdXRfU1VCU0VUXzIwIiwgImVwZW5fY2x1c18xIiwKICAgICAgICAgICAgICAgICAgICAiZXBlbl9jbHVzXzciLCAiZXBlbl9jbHVzXzE0IiksCiAgICAiaW5kaXJlY3QiID0gYygibnBjX1NVQlNFVF83IiwibnBjX1NVQlNFVF80IiwgImdsdXRfU1VCU0VUXzEwIiwiZ2x1dF9TVUJTRVRfMjIiLCAKICAgICAgICAgICAgICAgICAgICJnbHV0X1NVQlNFVF8xIiwgImdsdXRfU1VCU0VUXzExIiwgIm5wY19TVUJTRVRfMiIsIm5wY19TVUJTRVRfNCIsCiAgICAgICAgICAgICAgICAgICAibnBjX1NVQlNFVF83IiwgIm5wY19TVUJTRVRfMTMiLCAiZ2x1dF9TVUJTRVRfOSIsIm5wY19TVUJTRVRfNyIsCiAgICAgICAgICAgICAgICAgICAibnBjX1NVQlNFVF80IiwgImdsdXRfU1VCU0VUXzIxIiwgImdsdXRfU1VCU0VUXzIiLCJnbHV0X1NVQlNFVF8yNSIsCiAgICAgICAgICAgICAgICAgICAibnBjX1NVQlNFVF8xNCIsIm5wY19TVUJTRVRfMCIsImdsdXRfU1VCU0VUXzAiLCAiZ2x1dF9TVUJTRVRfNCIsCiAgICAgICAgICAgICAgICAgICAiZ2x1dF9TVUJTRVRfNSIsImdsdXRfU1VCU0VUXzciLCAibnBjX1NVQlNFVF83IiwibnBjX1NVQlNFVF8xMSIsCiAgICAgICAgICAgICAgICAgICAiZ2x1dF9TVUJTRVRfMTQiLCAiZ2x1dF9TVUJTRVRfMTUiLCAiZ2x1dF9TVUJTRVRfMTIiLCAiZ2x1dF9TVUJTRVRfMTYiLAogICAgICAgICAgICAgICAgICAgImdsdXRfU1VCU0VUXzE3IiwgImdsdXRfU1VCU0VUXzMiLCAibnBjX1NVQlNFVF8xIiwgIm5wY19TVUJTRVRfMyIsCiAgICAgICAgICAgICAgICAgICAibnBjX1NVQlNFVF85IikpCmBgYAoKU3Vic2V0IGRhdGEKCmBgYHtyfQpzdWJfc3JhdCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhyX2NlbGxzKSl7CiAgc3ViX3NyYXRbW25dXSA9IGF4X3NyYXRbLGF4X3NyYXQkY2VsbGNsdXN0ZXJzICVpbiUgYyhjb21tb25fY2VsbHMsIHJfY2VsbHNbW25dXSldCn0Kc3ViX3NyYXRbWyJhbGwiXV0gPSBheF9zcmF0WyxheF9zcmF0JGNlbGxjbHVzdGVycyAlaW4lIGMoY29tbW9uX2NlbGxzLCB1bmxpc3Qocl9jZWxscykpXQpgYGAKCkdldCBjbHVzdGVycwoKYGBge3J9Cm5wY3MgPSAxNQpmb3IobiBpbiBuYW1lcyhzdWJfc3JhdCkpewogIHByaW50KG4pCiAgc3ViX3NyYXRbW25dXSA9IE5vcm1hbGl6ZURhdGEoc3ViX3NyYXRbW25dXSkKICBzdWJfc3JhdFtbbl1dID0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc3ViX3NyYXRbW25dXSkKICBzdWJfc3JhdFtbbl1dID0gU2NhbGVEYXRhKHN1Yl9zcmF0W1tuXV0sIHZhcnMudG8ucmVncmVzcyA9IGMoIm5Db3VudF9STkEiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gc3ViX3NyYXRbW25dXSkpCiAgc3ViX3NyYXRbW25dXSA9IFJ1blBDQShzdWJfc3JhdFtbbl1dLCBmZWF0dXJlcyA9IFZhcmlhYmxlRmVhdHVyZXMob2JqZWN0ID0gc3ViX3NyYXRbW25dXSkpCiAgCiAgc3ViX3NyYXRbW25dXSA9IFJ1bkhhcm1vbnkoc3ViX3NyYXRbW25dXSwgImNoZW0iKQogIHN1Yl9zcmF0W1tuXV0gPSBSdW5VTUFQKHN1Yl9zcmF0W1tuXV0sIGRpbXMgPSAxOm5wY3MsIHJlZHVjdGlvbiA9ICJoYXJtb255IikKICAKICBzdWJfc3JhdFtbbl1dID0gRmluZE5laWdoYm9ycyhzdWJfc3JhdFtbbl1dLCBkaW1zID0gMTpucGNzLCByZWR1Y3Rpb24gPSAnaGFybW9ueScpCiAgc3ViX3NyYXRbW25dXSA9IEZpbmRDbHVzdGVycyhzdWJfc3JhdFtbbl1dLCByZXNvbHV0aW9uID0gc2VxKDAuMiwgMiwgMC4yKSkKfQpgYGAKCkxvYWQgVU1BUCBjb29yZAoKYGBge3J9CnVtYXBfbCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhzdWJfc3JhdClbMTo1XSl7CiAgbjIgPSBnc3ViKCJjbCIsICIiLCBuKQogIHVtYXBfbFtbbl1dID0gcmVhZC5jc3YocGFzdGUwKCJyZXN1bHRzL1JOQXZlbG9jaXR5L2dsdXRfY29yci9nbHV0Tm9FcF9zc18iLCBuMiwgIl91bWFwLmNzdiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSkKfQpgYGAKCgoKIyMgU2xpbmdzaG90CgoKYGBge3J9CiMgaGlwcG9jYW1wdXMKbiA9ICJoaXBwb2NhbXB1cyIKRGltUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJjZWxsY2x1c3RlcnMiLCBsYWJlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKRGltUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4wLjYiLCBsYWJlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKCiMgcnVuIHNsaW5nc2hvdApsaW4gPSBnZXRMaW5lYWdlcyhSZWR1Y3Rpb25zKHN1Yl9zcmF0W1tuXV0sICJ1bWFwIilAY2VsbC5lbWJlZGRpbmdzLCAKICAgICAgICAgICAgICAgICAgcGFzdGUwKCJjbCIsYXMuY2hhcmFjdGVyKHN1Yl9zcmF0W1tuXV1AbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjIpKSwgCiAgICAgICAgICAgICAgICAgIHN0YXJ0LmNsdXM9ICJjbDIxIiwgZW5kLmNsdXMgPSBjKCJjbDAiLCAiY2wxMiIpKQpsaW4gPSBnZXRMaW5lYWdlcyhSZWR1Y3Rpb25zKHN1Yl9zcmF0W1tuXV0sICJ1bWFwIilAY2VsbC5lbWJlZGRpbmdzLCAKICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHN1Yl9zcmF0W1tuXV1AbWV0YS5kYXRhJGNlbGxjbHVzdGVycyksIAogICAgICAgICAgICAgICAgICBzdGFydC5jbHVzPSAiZXBlbl9jbHVzXzMiLCBlbmQuY2x1cyA9IGMoImdsdXRfU1VCU0VUXzciLCAiZ2x1dF9TVUJTRVRfMCIpKQpuZW9sYWJzID0gcGFzdGUwKHN1Yl9zcmF0W1tuXV1AbWV0YS5kYXRhJGNlbGxjbHVzdGVycywgIl8iLAogICAgICAgICAgICAgICAgIHN1Yl9zcmF0W1tuXV1AbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjAuNCkKbGluID0gZ2V0TGluZWFnZXMoUmVkdWN0aW9ucyhzdWJfc3JhdFtbbl1dLCAidW1hcCIpQGNlbGwuZW1iZWRkaW5ncywgCiAgICAgICAgICAgICAgICAgIG5lb2xhYnMsIHN0YXJ0LmNsdXM9ICJlcGVuX2NsdXNfM185IiwgCiAgICAgICAgICAgICAgICAgIGVuZC5jbHVzID0gYygiZ2x1dF9TVUJTRVRfN180IiwgImdsdXRfU1VCU0VUXzBfMCIpKQpjcnYgPSBnZXRDdXJ2ZXMobGluKQoKIyBwbG90dGluZwpwdCA9IGFwcGx5KGNydkBhc3NheXNAZGF0YSRwc2V1ZG90aW1lLCAxLCBmdW5jdGlvbih4KSBtaW4oeFshaXMubmEoeCldKSkKcGxvdGNvbCA8LSBjb2xvcnNbY3V0KHB0LCBicmVha3M9MTAwKV0KcGxvdChSZWR1Y3Rpb25zKHN1Yl9zcmF0W1tuXV0sICJ1bWFwIilAY2VsbC5lbWJlZGRpbmdzLCAKICAgICBjb2wgPSBwbG90Y29sLCBhc3AgPSAxLCBwY2ggPSAxNikKbGluZXMoU2xpbmdzaG90RGF0YVNldChsaW4pLCBsd2QgPSAzLCBjb2wgPSAnYmxhY2snLCBzaG93LmNvbnN0cmFpbnRzID0gVFJVRSkKYGBgCgoKYGBge3J9CgoKCiMgbGMKbiA9ICJsYyIKRGltUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJjZWxsY2x1c3RlcnMiLCBsYWJlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKRGltUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4xLjQiLCBsYWJlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKCiMgcnVuIHNsaW5nc2hvdAojbGluID0gZ2V0TGluZWFnZXMoUmVkdWN0aW9ucyhzdWJfc3JhdFtbbl1dLCAidW1hcCIpQGNlbGwuZW1iZWRkaW5ncywgCiMgICAgICAgICAgICAgICAgICBwYXN0ZTAoImNsIixhcy5jaGFyYWN0ZXIoc3ViX3NyYXRbW25dXUBtZXRhLmRhdGEkUk5BX3Nubl9yZXMuMC44KSksIAojICAgICAgICAgICAgICAgICAgc3RhcnQuY2x1cz0gImNsMTIiLCBlbmQuY2x1cyA9IGMoImNsMCIsICJjbDQiKSkKbGluID0gZ2V0TGluZWFnZXMoUmVkdWN0aW9ucyhzdWJfc3JhdFtbbl1dLCAidW1hcCIpQGNlbGwuZW1iZWRkaW5ncywgCiAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihzdWJfc3JhdFtbbl1dQG1ldGEuZGF0YSRjZWxsY2x1c3RlcnMpLCAKICAgICAgICAgICAgICAgICAgc3RhcnQuY2x1cz0gImVwZW5fY2x1c18zIiwgZW5kLmNsdXMgPSBjKCJnbHV0X1NVQlNFVF8yIiwgImdsdXRfU1VCU0VUXzkiKSkKY3J2ID0gZ2V0Q3VydmVzKGxpbikKCiMgcGxvdHRpbmcKcHQgPSBhcHBseShjcnZAYXNzYXlzQGRhdGEkcHNldWRvdGltZSwgMSwgZnVuY3Rpb24oeCkgbWluKHhbIWlzLm5hKHgpXSkpCnBsb3Rjb2wgPC0gY29sb3JzW2N1dChwdCwgYnJlYWtzPTEwMCldCnBsb3QoUmVkdWN0aW9ucyhzdWJfc3JhdFtbbl1dLCAidW1hcCIpQGNlbGwuZW1iZWRkaW5ncywgCiAgICAgY29sID0gcGxvdGNvbCwgYXNwID0gMSwgcGNoID0gMTYpCmxpbmVzKFNsaW5nc2hvdERhdGFTZXQobGluKSwgbHdkID0gMywgY29sID0gJ2JsYWNrJywgc2hvdy5jb25zdHJhaW50cyA9IFRSVUUpCgojIGNsMTExCm4gPSAiY2wxMTEiCkRpbVBsb3Qoc3ViX3NyYXRbW25dXSwgZ3JvdXAuYnkgPSAiY2VsbGNsdXN0ZXJzIiwgbGFiZWwgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpCkRpbVBsb3Qoc3ViX3NyYXRbW25dXSwgZ3JvdXAuYnkgPSAiUk5BX3Nubl9yZXMuMS40IiwgbGFiZWwgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpCiMgZW9tZXMKbiA9ICJlb21lcyIKRGltUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJjZWxsY2x1c3RlcnMiLCBsYWJlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKRGltUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4xLjQiLCBsYWJlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKCiMgY2w4NjIwX2VwCm4gPSAiY2w4NjIwX2VwIgpEaW1QbG90KHN1Yl9zcmF0W1tuXV0sIGdyb3VwLmJ5ID0gImNlbGxjbHVzdGVycyIsIGxhYmVsID0gVCwgcmVkdWN0aW9uID0gInVtYXAiKQpEaW1QbG90KHN1Yl9zcmF0W1tuXV0sIGdyb3VwLmJ5ID0gIlJOQV9zbm5fcmVzLjEuNCIsIGxhYmVsID0gVCwgcmVkdWN0aW9uID0gInVtYXAiKQoKIyBhbGwKbiA9ICJhbGwiCkRpbVBsb3Qoc3ViX3NyYXRbW25dXSwgZ3JvdXAuYnkgPSAiY2VsbGNsdXN0ZXJzIiwgbGFiZWwgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpCkRpbVBsb3Qoc3ViX3NyYXRbW25dXSwgZ3JvdXAuYnkgPSAiUk5BX3Nubl9yZXMuMS40IiwgbGFiZWwgPSBULCByZWR1Y3Rpb24gPSAidW1hcCIpCgojIHJ1biBzbGluZ3Nob3QKI2xpbiA9IGdldExpbmVhZ2VzKFJlZHVjdGlvbnMoc3ViX3NyYXRbW25dXSwgInVtYXAiKUBjZWxsLmVtYmVkZGluZ3MsIAojICAgICAgICAgICAgICAgICAgcGFzdGUwKCJjbCIsYXMuY2hhcmFjdGVyKHN1Yl9zcmF0W1tuXV1AbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjAuOCkpLCAKIyAgICAgICAgICAgICAgICAgIHN0YXJ0LmNsdXM9ICJjbDEyIiwgZW5kLmNsdXMgPSBjKCJjbDAiLCAiY2w0IikpCmxpbiA9IGdldExpbmVhZ2VzKFJlZHVjdGlvbnMoc3ViX3NyYXRbW25dXSwgInVtYXAiKUBjZWxsLmVtYmVkZGluZ3MsIAogICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoc3ViX3NyYXRbW25dXUBtZXRhLmRhdGEkY2VsbGNsdXN0ZXJzKSwgCiAgICAgICAgICAgICAgICAgIHN0YXJ0LmNsdXM9ICJlcGVuX2NsdXNfMyIsIAogICAgICAgICAgICAgICAgICBlbmQuY2x1cyA9IGMoImdsdXRfU1VCU0VUXzciLCAiZ2x1dF9TVUJTRVRfMCIsICJnbHV0X1NVQlNFVF82IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnbHV0X1NVQlNFVF84IiwgImdsdXRfU1VCU0VUXzEwIiwgImdsdXRfU1VCU0VUXzIyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnbHV0X1NVQlNFVF8xIiwgImdsdXRfU1VCU0VUXzExIiwgImdsdXRfU1VCU0VUXzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdsdXRfU1VCU0VUXzkiKSkKY3J2ID0gZ2V0Q3VydmVzKGxpbikKCiMgcGxvdHRpbmcKcHQgPSBhcHBseShjcnZAYXNzYXlzQGRhdGEkcHNldWRvdGltZSwgMSwgZnVuY3Rpb24oeCkgbWluKHhbIWlzLm5hKHgpXSkpCnBsb3Rjb2wgPC0gY29sb3JzW2N1dChwdCwgYnJlYWtzPTEwMCldCnBsb3QoUmVkdWN0aW9ucyhzdWJfc3JhdFtbbl1dLCAidW1hcCIpQGNlbGwuZW1iZWRkaW5ncywgCiAgICAgY29sID0gcGxvdGNvbCwgYXNwID0gMSwgcGNoID0gMTYpCmxpbmVzKFNsaW5nc2hvdERhdGFTZXQobGluKSwgbHdkID0gMywgY29sID0gJ2JsYWNrJywgc2hvdy5jb25zdHJhaW50cyA9IFRSVUUpCmBgYAoKCgpgYGB7cn0KY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCgxMSwnU3BlY3RyYWwnKVstNl0pKDEwMCkKCiMgaGlwcG9jYW1wdXMKbiA9ICJoaXBwb2NhbXB1cyIKRGltUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJjZWxsY2x1c3RlcnMiLCBsYWJlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKRGltUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4xLjQiLCBsYWJlbCA9IFQsIHJlZHVjdGlvbiA9ICJ1bWFwIikKCiMgcnVuIHNsaW5nc2hvdApsaW4gPSBnZXRMaW5lYWdlcyh1bWFwX2xbW25dXSwgCiAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihzdWJfc3JhdFtbbl1dQG1ldGEuZGF0YSRjZWxsY2x1c3RlcnMpLCAKICAgICAgICAgICAgICAgICAgc3RhcnQuY2x1cz0gImVwZW5fY2x1c18zIiwgZW5kLmNsdXMgPSBjKCJnbHV0X1NVQlNFVF83IiwgImdsdXRfU1VCU0VUXzAiKSkKY3J2ID0gZ2V0Q3VydmVzKGxpbikKCiMgcGxvdHRpbmcKcHQgPSBhcHBseShjcnZAYXNzYXlzQGRhdGEkcHNldWRvdGltZSwgMSwgZnVuY3Rpb24oeCkgbWluKHhbIWlzLm5hKHgpXSkpCnBsb3Rjb2wgPC0gY29sb3JzW2N1dChwdCwgYnJlYWtzPTEwMCldCnBsb3QodW1hcF9sW1tuXV0sIAogICAgIGNvbCA9IHBsb3Rjb2wsIGFzcCA9IDEsIHBjaCA9IDE2KQpsaW5lcyhTbGluZ3Nob3REYXRhU2V0KGxpbiksIGx3ZCA9IDMsIGNvbCA9ICdibGFjaycsIHNob3cuY29uc3RyYWludHMgPSBUUlVFKQoKIyBsYwpuID0gImxjIgpEaW1QbG90KHN1Yl9zcmF0W1tuXV0sIGdyb3VwLmJ5ID0gImNlbGxjbHVzdGVycyIsIGxhYmVsID0gVCwgcmVkdWN0aW9uID0gInVtYXAiKQpEaW1QbG90KHN1Yl9zcmF0W1tuXV0sIGdyb3VwLmJ5ID0gIlJOQV9zbm5fcmVzLjEuNCIsIGxhYmVsID0gVCwgcmVkdWN0aW9uID0gInVtYXAiKQoKIyBydW4gc2xpbmdzaG90CmxpbiA9IGdldExpbmVhZ2VzKHVtYXBfbFtbbl1dLCAKICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHN1Yl9zcmF0W1tuXV1AbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjEuNCksIAogICAgICAgICAgICAgICAgICBzdGFydC5jbHVzPSAiMiIsIGVuZC5jbHVzID0gYygiMyIsICIxNSIpKQpjcnYgPSBnZXRDdXJ2ZXMobGluKQoKIyBwbG90dGluZwpwdCA9IGFwcGx5KGNydkBhc3NheXNAZGF0YSRwc2V1ZG90aW1lLCAxLCBmdW5jdGlvbih4KSBtaW4oeFshaXMubmEoeCldKSkKcGxvdGNvbCA8LSBjb2xvcnNbY3V0KHB0LCBicmVha3M9MTAwKV0KcGxvdCh1bWFwX2xbW25dXSwgCiAgICAgY29sID0gcGxvdGNvbCwgYXNwID0gMSwgcGNoID0gMTYpCmxpbmVzKFNsaW5nc2hvdERhdGFTZXQobGluKSwgbHdkID0gMywgY29sID0gJ2JsYWNrJywgc2hvdy5jb25zdHJhaW50cyA9IFRSVUUpCmBgYAoKCgpgYGB7cn0KeHh4ID0gUmVkdWN0aW9ucyhSdW5VTUFQKHN1Yl9zcmF0W1tuXV0sIGRpbXMgPSAxOm5wY3MsIAogICAgICAgICAgICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gImhhcm1vbnkiLCBuLmNvbXBvbmVudHMgPSAzKSwgInVtYXAiKQoKbGlicmFyeShwbG90bHkpCnBsb3RfbHkoeCA9IHN1Yl9zcmF0W1tuXV1AcmVkdWN0aW9ucyRoYXJtb255QGNlbGwuZW1iZWRkaW5nc1ssMV0sIHkgPSBzdWJfc3JhdFtbbl1dQHJlZHVjdGlvbnMkaGFybW9ueUBjZWxsLmVtYmVkZGluZ3NbLDJdLCB6ID0gc3ViX3NyYXRbW25dXUByZWR1Y3Rpb25zJGhhcm1vbnlAY2VsbC5lbWJlZGRpbmdzWyw0XSwKICAgICAgICB0eXBlPSJzY2F0dGVyM2QiLCBjb2xvcj1zdWJfc3JhdFtbbl1dJGNlbGxjbHVzdGVycywgc2l6ZSA9IDAuNzUpCmBgYAoKCgpgYGB7cn0Kc2NlID0gc2xpbmdzaG90KFJlZHVjdGlvbnMoc3ViX3NyYXRbW25dXSwgInVtYXAiKUBjZWxsLmVtYmVkZGluZ3MsIAogICAgICAgICAgICAgICAgcGFzdGUwKCJjbCIsYXMuY2hhcmFjdGVyKHN1Yl9zcmF0W1tuXV1AbWV0YS5kYXRhJFJOQV9zbm5fcmVzLjEpKSwgCiAgICAgICAgICAgICAgICBzdGFydC5jbHVzPSAiY2wxMCIsIGVuZC5jbHVzID0gYygiY2wwIiwgImNsMyIpKQpVTUFQUGxvdChzdWJfc3JhdFtbbl1dLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4wLjYiLCBsYWJlbCA9IFQpCgpsaWJyYXJ5KGdyRGV2aWNlcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoMTEsJ1NwZWN0cmFsJylbLTZdKSgxMDApCnB0ID0gYXBwbHkoc2NlQGFzc2F5c0BkYXRhJHBzZXVkb3RpbWUsIDEsIGZ1bmN0aW9uKHgpIG1pbih4WyFpcy5uYSh4KV0pKQpwbG90Y29sIDwtIGNvbG9yc1tjdXQocHQsIGJyZWFrcz0xMDApXQoKcGxvdChSZWR1Y3Rpb25zKHN1Yl9zcmF0W1tuXV0sICJ1bWFwIilAY2VsbC5lbWJlZGRpbmdzLCBjb2wgPSBwbG90Y29sLCBwY2g9MTYsIGFzcCA9IDEpCmxpbmVzKFNsaW5nc2hvdERhdGFTZXQoc2NlKSwgbHdkPTIsIGNvbD0nYmxhY2snKQpEaW1QbG90KHN1Yl9zcmF0W1tuXV0sIGdyb3VwLmJ5ID0gImNlbGxjbHVzdGVycyIsIHJlZHVjdGlvbiA9ICJ1bWFwIikKYGBgCgoKCiMjIFVSRApQcmVwYXJlIFVSRCBvYmplY3RzCgpgYGB7cn0Kc3ViX3VyZCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhzdWJfc3JhdClbMTo1XSl7CiAgc3ViX3VyZFtbbl1dID0gc3ViX3NyYXRbW25dXQogIHN1Yl91cmRbW25dXSA9IGNyZWF0ZVVSRChjb3VudC5kYXRhID0gc3ViX3VyZFtbbl1dQGFzc2F5cyRSTkFAY291bnRzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YSA9IHN1Yl91cmRbW25dXUBtZXRhLmRhdGEsIG1pbi5jZWxscz0zLCBtaW4uY291bnRzPTMpCiAgc3ViX3VyZFtbbl1dQGdyb3VwLmlkcyRjZWxsY2x1c3RlcnMgPSBzdWJfdXJkW1tuXV1AbWV0YSRjZWxsY2x1c3RlcnMKICBzdWJfdXJkW1tuXV1AZ3JvdXAuaWRzJHN1YmNsYXNzZXMgPSBzdWJfdXJkW1tuXV1AbWV0YSRzdWJjbGFzc2VzCn0KYGBgCgpDYWxjdWxhdGUgRFJzCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoc3ViX3VyZCkpewogIHN1Yl91cmRbW25dXUB2YXIuZ2VuZXMgPSBmaW5kVmFyaWFibGVHZW5lcyhzdWJfdXJkW1tuXV0sIHNldC5vYmplY3QudmFyLmdlbmVzPUYsZG8ucGxvdD1GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlmZkNWLmN1dG9mZj0wLjMsIG1lYW4ubWluPS4wMDUsIG1lYW4ubWF4PTEwMCkKCiAgc3ViX3VyZFtbbl1dID0gY2FsY1BDQShzdWJfdXJkW1tuXV0sIG1wLmZhY3RvciA9IDIpCiAgcGNTRFBsb3Qoc3ViX3VyZFtbbl1dKQogIHN1Yl91cmRbW25dXSA9IGNhbGNUc25lKG9iamVjdCA9IHN1Yl91cmRbW25dXSkKICBwbG90RGltKHN1Yl91cmRbW25dXSwgImNlbGxjbHVzdGVycyIpCiAgCiAgc3ViX3VyZFtbbl1dID0gY2FsY0RNKHN1Yl91cmRbW25dXSwga25uID0gMTUwLCBzaWdtYT0xNikKICAKICBwbG90RGltQXJyYXkoc3ViX3VyZFtbbl1dLCByZWR1Y3Rpb24udXNlID0gImRtIiwgZGltcy50by5wbG90ID0gMTo4LCAKICAgICAgICAgICAgIG91dGVyLnRpdGxlID0gIkRpZmZ1c2lvbiBNYXAgKFNpZ21hIDE2LCAxNTAgTk5zKTogU3RhZ2UiLCBsYWJlbD0iY2VsbGNsdXN0ZXJzIiwKICAgICAgICAgICAgIHBsb3QudGl0bGU9IiIsIGxlZ2VuZD1GKQoKICBwbG90RGltKHN1Yl91cmRbW25dXSwgImNlbGxjbHVzdGVycyIsIHRyYW5zaXRpb25zLnBsb3QgPSAxMDAwMCkKfQpgYGAKClBzZXVkb3RpbWUgY2FsY3VsYXRpb24KCmBgYHtyfQpmbG9vZHNfbCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhzdWJfdXJkKSl7CiAgcHJpbnQobikKICAjIEhlcmUgd2UgdXNlIGFsbCBjZWxscyBmcm9tIHRoZSBmaXJzdCBzdGFnZSBhcyB0aGUgcm9vdAogIHJvb3QuY2VsbHMgPSBjZWxsc0luQ2x1c3RlcihzdWJfdXJkW1tuXV0sIGNsdXN0ZXJpbmcgPSAiY2VsbGNsdXN0ZXJzIiwgImVwZW5fY2x1c18zIikKICAKICAjIFRoZW4gd2UgcnVuICdmbG9vZCcgc2ltdWxhdGlvbnMKICBmbG9vZHMgPSBmbG9vZFBzZXVkb3RpbWUoc3ViX3VyZFtbbl1dLCByb290LmNlbGxzID0gcm9vdC5jZWxscywgbj0yNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmltdW0uY2VsbHMuZmxvb2RlZCA9IDIsIHZlcmJvc2U9RikKICAjIGZpeCBleGNlc3NpdmUgTkEKICBmbG9vZHMgPSBmbG9vZHNbLGNvbFN1bXMoIWlzLm5hKGZsb29kcykpPjEwMDBdCiAgCiAgIyBUaGUgd2UgcHJvY2VzcyB0aGUgc2ltdWxhdGlvbnMgaW50byBhIHBzZXVkb3RpbWUKICBzdWJfdXJkW1tuXV0gPSBmbG9vZFBzZXVkb3RpbWVQcm9jZXNzKHN1Yl91cmRbW25dXSwgZmxvb2RzLCBmbG9vZHMubmFtZT0icHNldWRvdGltZSIpCiAgCiAgcHNldWRvdGltZVBsb3RTdGFiaWxpdHlPdmVyYWxsKHN1Yl91cmRbW25dXSkKICAKICBwbG90RGlzdHMoc3ViX3VyZFtbbl1dLCAicHNldWRvdGltZSIsICJjZWxsY2x1c3RlcnMiLCBwbG90LnRpdGxlPSJQc2V1ZG90aW1lIGJ5IHN0YWdlIikKICAKICBmbG9vZHNfbFtbbl1dID0gZmxvb2RzCn0KYGBgCgpgYGB7cn0KZW5kX2NjID0gbGlzdCgiaGlwcG9jYW1wdXMiID0gYygiZ2x1dF9TVUJTRVRfMCIsICJnbHV0X1NVQlNFVF83IiksCiAgICAgICAgICAgICAgImxjIiA9IGMoImdsdXRfU1VCU0VUXzkiLCJnbHV0X1NVQlNFVF8yIiksCiAgICAgICAgICAgICAgImNsMTExIiA9IGMoImdsdXRfU1VCU0VUXzEiLCAiZ2x1dF9TVUJTRVRfMTEiKSwKICAgICAgICAgICAgICAiZW9tZXMiID0gYygiZ2x1dF9TVUJTRVRfMTAiLCJnbHV0X1NVQlNFVF8yMiIpLAogICAgICAgICAgICAgICJjbDg2MjBfZXAiID0gYygiZ2x1dF9TVUJTRVRfOCIsImdsdXRfU1VCU0VUXzYiKSkKc3Vic2V0X2RhdF9sID0gbGlzdCgpCmZsb29kc180X2wgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoc3ViX3VyZCkpewogIHByaW50KG4pCiAgIyBIZXJlIHdlIHVzZSBhbGwgY2VsbHMgZnJvbSB0aGUgZmlyc3Qgc3RhZ2UgYXMgdGhlIHJvb3QKICByb290LmNlbGxzID0gY2VsbHNJbkNsdXN0ZXIoc3ViX3VyZFtbbl1dLCBjbHVzdGVyaW5nID0gImNlbGxjbHVzdGVycyIsICJlcGVuX2NsdXNfNCIpCiAgCiAgIyBUaGVuIHdlIHJ1biAnZmxvb2QnIHNpbXVsYXRpb25zCiAgZmxvb2RzID0gZmxvb2RQc2V1ZG90aW1lKHN1Yl91cmRbW25dXSwgcm9vdC5jZWxscyA9IHJvb3QuY2VsbHMsIG49MjUwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5pbXVtLmNlbGxzLmZsb29kZWQgPSAyLCB2ZXJib3NlPUYpCiAgIyBmaXggZXhjZXNzaXZlIE5BCiAgZmxvb2RzID0gZmxvb2RzWyxjb2xTdW1zKCFpcy5uYShmbG9vZHMpKT4xMDAwXQogIAogICMgVGhlIHdlIHByb2Nlc3MgdGhlIHNpbXVsYXRpb25zIGludG8gYSBwc2V1ZG90aW1lCiAgc3ViX3VyZFtbbl1dID0gZmxvb2RQc2V1ZG90aW1lUHJvY2VzcyhzdWJfdXJkW1tuXV0sIGZsb29kcywgZmxvb2RzLm5hbWU9InBzZXVkb3RpbWVfNCIpCiAgCiAgcHNldWRvdGltZVBsb3RTdGFiaWxpdHlPdmVyYWxsKHN1Yl91cmRbW25dXSkKICAKICBwbG90RGlzdHMoc3ViX3VyZFtbbl1dLCAicHNldWRvdGltZV80IiwgImNlbGxjbHVzdGVycyIsIHBsb3QudGl0bGU9IlBzZXVkb3RpbWUgYnkgc3RhZ2UiKQogIAogICMgQ3JlYXRlIGEgc3Vic2V0dGVkIG9iamVjdCBvZiBqdXN0IHRob3NlIGNlbGxzIGZyb20gdGhlIGZpbmFsIHN0YWdlCiAgc3Vic2V0ZGF0ID0gdXJkU3Vic2V0KHN1Yl91cmRbW25dXSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxzLmtlZXA9Y2VsbHNJbkNsdXN0ZXIoc3ViX3VyZFtbbl1dLCAiY2VsbGNsdXN0ZXJzIiwgZW5kX2NjW1tuXV0pKQogIHN1YnNldF9kYXRfbFtbbl1dID0gc3Vic2V0ZGF0CiAgZmxvb2RzXzRfbFtbbl1dID0gZmxvb2RzCn0KYGBgCgpTYXZlCgpgYGB7cn0Kc2F2ZShzdWJzZXRfZGF0X2wsIGZsb29kc19sLCBzdWJfdXJkLCBmbG9vZHNfNF9sLCBmaWxlID0gImRhdGEvcHJvY2Vzc2VkL1VSRC9VUkRfbGlzdHMuUkRhdGEiKQpgYGAKCkRldGVybWluZSBuZXcgaWRlbnRpdGllcyBmb3IgZW5kIHRpcHMKCmBgYHtyfQpheGlhbHNfbCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhzdWJfdXJkKSl7CiAgIyBVc2UgdGhlIHZhcmlhYmxlIGdlbmVzIHRoYXQgd2VyZSBjYWxjdWxhdGVkIG9ubHkgb24gdGhlIGZpbmFsIGdyb3VwIG9mIHN0YWdlcyAod2hpY2gKICAjIGNvbnRhaW4gdGhlIGxhc3Qgc3RhZ2UpLgogIHN1YnNldF9kYXRfbFtbbl1dQHZhci5nZW5lcyA9IHN1Yl91cmRbW25dXUB2YXIuZ2VuZXNbc3ViX3VyZFtbbl1dQHZhci5nZW5lcyAlaW4lIHJvd25hbWVzKHN1YnNldF9kYXRfbFtbbl1dQGxvZ3VweC5kYXRhKV0KICAKICAjIENhbGN1bGF0ZSBQQ0EgYW5kIHRTTkUKICBzdWJzZXRfZGF0X2xbW25dXSA9IGNhbGNQQ0Eoc3Vic2V0X2RhdF9sW1tuXV0sIG1wLmZhY3RvciA9IDEuNSkKICBwY1NEUGxvdChzdWJzZXRfZGF0X2xbW25dXSkKICAKICBzZXQuc2VlZCgyMCkKICBzdWJzZXRkYXQgPSBjYWxjVHNuZShzdWJzZXRfZGF0X2xbW25dXSkKICAKICAjIENhbGN1bGF0ZSBncmFwaCBjbHVzdGVyaW5nIG9mIHRoZXNlIGNlbGxzCiAgc3Vic2V0X2RhdF9sW1tuXV0gPSBncmFwaENsdXN0ZXJpbmcoc3Vic2V0X2RhdF9sW1tuXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bS5ubiA9IDUwLCBkby5qYWNjYXJkPVQsIG1ldGhvZD0iTG91dmFpbiIpCiAgcGxvdERpbShzdWJzZXRfZGF0X2xbW25dXSwgIkxvdXZhaW4tNTAiLCAKICAgICAgICAgIHBsb3QudGl0bGUgPSAiTG91dmFpbiAoNTAgTk4pIGdyYXBoIGNsdXN0ZXJpbmciLCBwb2ludC5zaXplPTMpCiAgcGxvdERpbShzdWJzZXRfZGF0X2xbW25dXSwgImNlbGxjbHVzdGVycyIsIHBsb3QudGl0bGUgPSAiY2VsbGNsdXN0ZXJzIiwgcG9pbnQuc2l6ZT0zKQogIAogIAogICMgQ29weSBjbHVzdGVyIGlkZW50aXRpZXMgZnJvbSBheGlhbC42c29taXRlIG9iamVjdCB0byBhIG5ldyBjbHVzdGVyaW5nICgidGlwLmNsdXN0ZXJzIikgaW4gdGhlIGZ1bGwgYXhpYWwgb2JqZWN0LgogIHN1Yl91cmRbW25dXUBncm91cC5pZHNbcm93bmFtZXMoc3Vic2V0X2RhdF9sW1tuXV1AZ3JvdXAuaWRzKSwgInRpcC5jbHVzdGVycyJdID0gc3Vic2V0X2RhdF9sW1tuXV1AZ3JvdXAuaWRzJGBMb3V2YWluLTUwYAogIAogICMgRGV0ZXJtaW5lIHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBsb2dpc3RpYyB1c2VkIHRvIGJpYXMgdGhlIHRyYW5zaXRpb24gcHJvYmFiaWxpdGllcy4gCiAgIyBUaGUgcHJvY2VkdXJlIGlzIHJlbGF0aXZlbHkgcm9idXN0IHRvIHRoaXMgcGFyYW1ldGVyLCBidXQgdGhlIGNlbGwgbnVtYmVycyBtYXkgbmVlZCB0byBiZQogICMgbW9kaWZpZWQgZm9yIGxhcmdlciBvciBzbWFsbGVyIGRhdGEgc2V0cy4KICBheGlhbC5wdGxvZ2lzdGljID0gcHNldWRvdGltZURldGVybWluZUxvZ2lzdGljKHN1Yl91cmRbW25dXSwgInBzZXVkb3RpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW1hbC5jZWxscy5mb3J3YXJkPTIwLCBtYXguY2VsbHMuYmFjaz0yMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLnBsb3QgPSBUKQogIAogIGF4aWFsLmJpYXNlZC50bSA9IGFzLm1hdHJpeChwc2V1ZG90aW1lV2VpZ2h0VHJhbnNpdGlvbk1hdHJpeChzdWJfdXJkW1tuXV0sICJwc2V1ZG90aW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9naXN0aWMucGFyYW1zPWF4aWFsLnB0bG9naXN0aWMpKQogIAogIGF4aWFsc19sW1tuXV0gPSBsaXN0KCJsb2ciID0gYXhpYWwucHRsb2dpc3RpYywgInRtIiA9IGF4aWFsLmJpYXNlZC50bSkKfQpgYGAKCmBgYHtyfQpheGlhbHNfNF9sID0gbGlzdCgpCmZvcihuIGluIG5hbWVzKHN1Yl91cmQpKXsKICAjIFVzZSB0aGUgdmFyaWFibGUgZ2VuZXMgdGhhdCB3ZXJlIGNhbGN1bGF0ZWQgb25seSBvbiB0aGUgZmluYWwgZ3JvdXAgb2Ygc3RhZ2VzICh3aGljaAogICMgY29udGFpbiB0aGUgbGFzdCBzdGFnZSkuCiAgc3Vic2V0X2RhdF9sW1tuXV1AdmFyLmdlbmVzID0gc3ViX3VyZFtbbl1dQHZhci5nZW5lc1tzdWJfdXJkW1tuXV1AdmFyLmdlbmVzICVpbiUgcm93bmFtZXMoc3Vic2V0X2RhdF9sW1tuXV1AbG9ndXB4LmRhdGEpXQogIAogICMgQ2FsY3VsYXRlIFBDQSBhbmQgdFNORQogIHN1YnNldF9kYXRfbFtbbl1dID0gY2FsY1BDQShzdWJzZXRfZGF0X2xbW25dXSwgbXAuZmFjdG9yID0gMS41KQogIHBjU0RQbG90KHN1YnNldF9kYXRfbFtbbl1dKQogIAogIHNldC5zZWVkKDIwKQogIHN1YnNldGRhdCA9IGNhbGNUc25lKHN1YnNldF9kYXRfbFtbbl1dKQogIAogICMgQ2FsY3VsYXRlIGdyYXBoIGNsdXN0ZXJpbmcgb2YgdGhlc2UgY2VsbHMKICBzdWJzZXRfZGF0X2xbW25dXSA9IGdyYXBoQ2x1c3RlcmluZyhzdWJzZXRfZGF0X2xbW25dXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtLm5uID0gNTAsIGRvLmphY2NhcmQ9VCwgbWV0aG9kPSJMb3V2YWluIikKICBwbG90RGltKHN1YnNldF9kYXRfbFtbbl1dLCAiTG91dmFpbi01MCIsIAogICAgICAgICAgcGxvdC50aXRsZSA9ICJMb3V2YWluICg1MCBOTikgZ3JhcGggY2x1c3RlcmluZyIsIHBvaW50LnNpemU9MykKICBwbG90RGltKHN1YnNldF9kYXRfbFtbbl1dLCAiY2VsbGNsdXN0ZXJzIiwgcGxvdC50aXRsZSA9ICJjZWxsY2x1c3RlcnMiLCBwb2ludC5zaXplPTMpCiAgCiAgCiAgIyBDb3B5IGNsdXN0ZXIgaWRlbnRpdGllcyBmcm9tIGF4aWFsLjZzb21pdGUgb2JqZWN0IHRvIGEgbmV3IGNsdXN0ZXJpbmcgKCJ0aXAuY2x1c3RlcnMiKSBpbiB0aGUgZnVsbCBheGlhbCBvYmplY3QuCiAgc3ViX3VyZFtbbl1dQGdyb3VwLmlkc1tyb3duYW1lcyhzdWJzZXRfZGF0X2xbW25dXUBncm91cC5pZHMpLCAidGlwLmNsdXN0ZXJzIl0gPSBzdWJzZXRfZGF0X2xbW25dXUBncm91cC5pZHMkYExvdXZhaW4tNTBgCiAgCiAgIyBEZXRlcm1pbmUgdGhlIHBhcmFtZXRlcnMgb2YgdGhlIGxvZ2lzdGljIHVzZWQgdG8gYmlhcyB0aGUgdHJhbnNpdGlvbiBwcm9iYWJpbGl0aWVzLiAKICAjIFRoZSBwcm9jZWR1cmUgaXMgcmVsYXRpdmVseSByb2J1c3QgdG8gdGhpcyBwYXJhbWV0ZXIsIGJ1dCB0aGUgY2VsbCBudW1iZXJzIG1heSBuZWVkIHRvIGJlCiAgIyBtb2RpZmllZCBmb3IgbGFyZ2VyIG9yIHNtYWxsZXIgZGF0YSBzZXRzLgogIGF4aWFsLnB0bG9naXN0aWMgPSBwc2V1ZG90aW1lRGV0ZXJtaW5lTG9naXN0aWMoc3ViX3VyZFtbbl1dLCAicHNldWRvdGltZV80IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdGltYWwuY2VsbHMuZm9yd2FyZD0yMCwgbWF4LmNlbGxzLmJhY2s9MjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkby5wbG90ID0gVCkKICAKICBheGlhbC5iaWFzZWQudG0gPSBhcy5tYXRyaXgocHNldWRvdGltZVdlaWdodFRyYW5zaXRpb25NYXRyaXgoc3ViX3VyZFtbbl1dLCAicHNldWRvdGltZV80IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9naXN0aWMucGFyYW1zPWF4aWFsLnB0bG9naXN0aWMpKQogIAogIGF4aWFsc180X2xbW25dXSA9IGxpc3QoImxvZyIgPSBheGlhbC5wdGxvZ2lzdGljLCAidG0iID0gYXhpYWwuYmlhc2VkLnRtKQp9CmBgYAoKUmFuZG9tIHdhbGsgc2ltdWxhdGlvbnMKCmBgYHtyfQpzdWJmaXhfbCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhzdWJfdXJkKSl7CiAgc3Vic2V0Zml4ID0gdXJkU3Vic2V0KHN1Yl91cmRbW25dXSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxzLmtlZXA9cm93bmFtZXMoc3ViX3VyZFtbbl1dQG1ldGEpW3Jvd25hbWVzKHN1Yl91cmRbW25dXUBtZXRhKSVpbiVyb3duYW1lcyhheGlhbHNfbFtbbl1dW1sidG0iXV0pXSkKICByb290LmNlbGxzID0gY2VsbHNJbkNsdXN0ZXIoc3ViX3VyZFtbbl1dLCBjbHVzdGVyaW5nID0gImNlbGxjbHVzdGVycyIsICJlcGVuX2NsdXNfMyIpCiAgCiAgIyBTaW11bGF0ZSB0aGUgYmlhc2VkIHJhbmRvbSB3YWxrcyBmcm9tIGVhY2ggdGlwCiAgYXhpYWwud2Fsa3MgPSBzaW11bGF0ZVJhbmRvbVdhbGtzRnJvbVRpcHMoc3Vic2V0Zml4LCB0aXAuZ3JvdXAuaWQ9InRpcC5jbHVzdGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9vdC5jZWxscz1yb290LmNlbGxzLCBuLnBlci50aXAgPSAyNTAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2l0aW9uLm1hdHJpeCA9IGF4aWFsc19sW1tuXV1bWyJ0bSJdXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9vdC52aXNpdHMgPSAxLCBtYXguc3RlcHMgPSAxMDAwMCwgdmVyYm9zZSA9IEYpCiAgCiAgIyBQcm9jZXNzIHRoZSBiaWFzZWQgcmFuZG9tIHdhbGtzIGludG8gdmlzaXRhdGlvbiBmcmVxdWVuY2llcwogIHN1YnNldGZpeCA9IHByb2Nlc3NSYW5kb21XYWxrc0Zyb21UaXBzKHN1YnNldGZpeCwgYXhpYWwud2Fsa3MsIHZlcmJvc2UgPSBGKQogIAogIGF4aWFsc19sW1tuXV1bWyJ3YWxrcyJdXSA9IGF4aWFsLndhbGtzCiAgc3ViZml4X2xbW25dXSA9IHN1YnNldGZpeAp9CmBgYAoKYGBge3J9CnN1YmZpeF80X2wgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoc3ViX3VyZCkpewogIHN1YnNldGZpeCA9IHVyZFN1YnNldChzdWJfdXJkW1tuXV0sIAogICAgICAgICAgICAgICAgICAgICAgICBjZWxscy5rZWVwPXJvd25hbWVzKHN1Yl91cmRbW25dXUBtZXRhKVtyb3duYW1lcyhzdWJfdXJkW1tuXV1AbWV0YSklaW4lcm93bmFtZXMoYXhpYWxzXzRfbFtbbl1dW1sidG0iXV0pXSkKICByb290LmNlbGxzID0gY2VsbHNJbkNsdXN0ZXIoc3ViX3VyZFtbbl1dLCBjbHVzdGVyaW5nID0gImNlbGxjbHVzdGVycyIsICJlcGVuX2NsdXNfNCIpCiAgCiAgIyBTaW11bGF0ZSB0aGUgYmlhc2VkIHJhbmRvbSB3YWxrcyBmcm9tIGVhY2ggdGlwCiAgYXhpYWwud2Fsa3MgPSBzaW11bGF0ZVJhbmRvbVdhbGtzRnJvbVRpcHMoc3Vic2V0Zml4LCB0aXAuZ3JvdXAuaWQ9InRpcC5jbHVzdGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9vdC5jZWxscz1yb290LmNlbGxzLCBuLnBlci50aXAgPSAyNTAwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFuc2l0aW9uLm1hdHJpeCA9IGF4aWFsc180X2xbW25dXVtbInRtIl1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb290LnZpc2l0cyA9IDEsIG1heC5zdGVwcyA9IDEwMDAwLCB2ZXJib3NlID0gRikKICAKICAjIFByb2Nlc3MgdGhlIGJpYXNlZCByYW5kb20gd2Fsa3MgaW50byB2aXNpdGF0aW9uIGZyZXF1ZW5jaWVzCiAgc3Vic2V0Zml4ID0gcHJvY2Vzc1JhbmRvbVdhbGtzRnJvbVRpcHMoc3Vic2V0Zml4LCBheGlhbC53YWxrcywgdmVyYm9zZSA9IEYpCiAgCiAgYXhpYWxzXzRfbFtbbl1dW1sid2Fsa3MiXV0gPSBheGlhbC53YWxrcwogIHN1YmZpeF80X2xbW25dXSA9IHN1YnNldGZpeAp9CmBgYAoKU2F2ZQoKYGBge3J9CnNhdmUoc3Vic2V0X2RhdF9sLCBmbG9vZHNfbCwgZmxvb2RzXzRfbCwgc3ViX3VyZCwgYXhpYWxzX2wsIHN1YmZpeF9sLCBheGlhbHNfNF9sLCBzdWJmaXhfNF9sLAogICAgIGZpbGUgPSAiZGF0YS9wcm9jZXNzZWQvVVJEL1VSRF9saXN0cy5SRGF0YSIpCmBgYAoKQ2hlY2sgdGlwIGNsdXN0ZXJzCgpgYGB7ciwgZmlnLmhlaWdodD0zLjUsIGZpZy53aWR0aD04fQptYXhfdmFscyA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhzdWJmaXhfNF9sKSl7CiAgcGx0MSA9IHBsb3REaW0oc3ViZml4XzRfbFtbbl1dLCAiY2VsbGNsdXN0ZXJzIiwgcGxvdC50aXRsZT1uKQogIHBsdDIgPSBwbG90RGltKHN1YmZpeF80X2xbW25dXSwgInRpcC5jbHVzdGVycyIsIHBsb3QudGl0bGU9bikKICBwcmludChwbHQxK3BsdDIpCiAgCiAgbWF4X3ZhbHNbW25dXSA9IHNvcnQodGFwcGx5KHN1YmZpeF80X2xbW25dXUBwc2V1ZG90aW1lJHBzZXVkb3RpbWVfNCwKICAgICAgICAgICAgICAgICAgICBzdWJmaXhfNF9sW1tuXV1AZ3JvdXAuaWRzJHRpcC5jbHVzdGVycywgbWF4KSkKfQpgYGAKClBsb3QgdHJlZXMKCmBgYHtyLCBmaWcuaGVpZ2h0PTMuNSwgZmlnLndpZHRoPTR9CmNsX3RpcHMgPSBsaXN0KGMoIjYiLCAiMSIpLCBjKCIxMSIsICIyIiksIGMoIjYiLCAiMSIpLCBjKCIyIiwgIjMiKSwgYygiNiIsICI1IikpCm5hbWVzKGNsX3RpcHMpID0gbmFtZXMoc3ViZml4XzRfbCkKdHJlZV9wbHRfbCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhzdWJmaXhfNF9sKSl7CiAgIyBMb2FkIHRoZSBjZWxscyB1c2VkIGZvciBlYWNoIHRpcCBpbnRvIHRoZSBVUkQgb2JqZWN0CiAgYXhpYWwudHJlZSA9IGxvYWRUaXBDZWxscyhzdWJmaXhfNF9sW1tuXV0sICJ0aXAuY2x1c3RlcnMiKQoKICAjIEJ1aWxkIHRoZSB0cmVlCiAgdGVzdCA9IGJ1aWxkVHJlZShheGlhbC50cmVlLCBwc2V1ZG90aW1lID0gInBzZXVkb3RpbWVfNCIsIHRpcHMudXNlPWNsX3RpcHNbW25dXSwgCiAgICAgICAgICAgICAgICAgIGRpdmVyZ2VuY2UubWV0aG9kID0gInByZWZlcmVuY2UiLCBjZWxscy5wZXIucHNldWRvdGltZS5iaW4gPSAyMCwKICAgICAgICAgICAgICAgICAgYmlucy5wZXIucHNldWRvdGltZS53aW5kb3cgPSA4LCBzYXZlLmFsbC5icmVha3BvaW50LmluZm8gPSBULAogICAgICAgICAgICAgICAgICBwLnRocmVzaD0wLjAwMSxtaW5pbXVtLnZpc2l0cyA9IDIpCiAgCiAgcGx0MSA9IHBsb3RUcmVlKHRlc3QsICJ0aXAuY2x1c3RlcnMiKQogIHBsdDIgPSBwbG90VHJlZSh0ZXN0LCAiY2VsbGNsdXN0ZXJzIikKICAKICAjIGFkZCBsYWJlbHMKICBjbGF5b3V0ID0gdGVzdEB0cmVlJGNlbGwubGF5b3V0CiAgY2xheW91dCRjdCA9IHRlc3RAbWV0YVtyb3duYW1lcyhjbGF5b3V0KSwiY2VsbGNsdXN0ZXJzIl0KICBsYWJkZiA9IGRhdGEuZnJhbWUoeCA9IHRhcHBseShjbGF5b3V0JHgsIGNsYXlvdXQkY3QsIG1lYW4pLAogICAgICAgICAgICAgICAgICAgICB5ID0gdGFwcGx5KGNsYXlvdXQkeSwgY2xheW91dCRjdCwgbWVhbikpCiAgbGFiZGYkbGFiID0gcm93bmFtZXMobGFiZGYpCiAgY29uZHggPSBsYWJkZiR5PG1pbih0ZXN0QHRyZWUkc2VnbWVudC5wc2V1ZG90aW1lLmxpbWl0cyRlbmQpCiAgbGFiZGZbIWNvbmR4LCJ4Il0gPSByb3VuZChsYWJkZlshY29uZHgsIngiXSwgMCkKICBsYWJkZltjb25keCwieCJdID0gMC41CiAgCiAgcGx0MyA9IHBsdDIrc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xzX2NjLCBsaW1pdHMgPSBmb3JjZSkrCiAgICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPSBsYWJkZiwgbWFwcGluZyA9IGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiKSwgc2l6ZSA9IDIuMiwgCiAgICAgICAgICAgICAgIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAuMSwgImxpbmVzIiksIGJveC5wYWRkaW5nID0gdW5pdCgwLjEsICJsaW5lcyIpKSsKICAgIGxhYnModGl0bGUgPSBuKSsKICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC4yLCAibGluZXMiKSwKICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQogIHByaW50KHBsdDMpCiAgdHJlZV9wbHRfbFtbbl1dID0gcGx0Mwp9CmBgYAoKU2F2ZSB0cmVlcwoKYGBge3J9CmZvcihuIGluIG5hbWVzKHRyZWVfcGx0X2wpKXsKICBwZGYocGFzdGUwKCJyZXN1bHRzL3BzZXVkb3RpbWUvU1NfIiwgbiwgIl90cmVlLnBkZiIpLCBoZWlnaHQgPSAzLjUsIHdpZHRoID0gNCkKICBwcmludCh0cmVlX3BsdF9sW1tuXV0pCiAgZGV2Lm9mZigpCn0KYGBgCgpMb2FkIFJOQSB2ZWxvY2l0eSBwc2V1ZG90aW1lCgpgYGB7cn0KZ2x1dF9kYXRfZGYgPSByZWFkLmNzdigicmVzdWx0cy9STkF2ZWxvY2l0eS9nbHV0X2NvcnIvaGVhdG1hcF9nZW4vZ2x1dF9kYXRfZGYuY3N2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwgcm93Lm5hbWVzID0gMSkKbmV3Y2VsbG5hbWVzID0gcm93bmFtZXMoZ2x1dF9kYXRfZGYpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9EMSIsICItMV8xXzUiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9EMiIsICItMV8yXzUiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9MMSIsICItMV8zXzUiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9MMiIsICItMV80XzUiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9NMSIsICItMV81XzUiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9NMiIsICItMV82XzUiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9hMV8xIiwgIi0xXzEiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9hMV8yIiwgIi0xXzIiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9hM18xIiwgIi0xXzMiLCBuZXdjZWxsbmFtZXMpCm5ld2NlbGxuYW1lcyA9IGdzdWIoIl9hM18yIiwgIi0xXzQiLCBuZXdjZWxsbmFtZXMpCnJvd25hbWVzKGdsdXRfZGF0X2RmKSA9IG5ld2NlbGxuYW1lcwpgYGAKClBsb3QgcHNldWRvdGltZSBjb21wYXJpc29ucwoKYGBge3J9CnBsdF9jb21wX2wgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoc3ViZml4X2wpKXsKICBwbG90X2RmID0gbWVyZ2UoZ2x1dF9kYXRfZGYsIHN1YmZpeF80X2xbW25dXUBwc2V1ZG90aW1lLCBieSA9IDApCiAgcGxvdF9kZiRzdWJjbGFzc2VzID0gc3ViZml4XzRfbFtbbl1dQG1ldGFbcGxvdF9kZiRSb3cubmFtZXMsInN1YmNsYXNzZXMiXQogIHBsb3RfZGYkc3ViY2xhc3NlcyA9IGZhY3RvcihwbG90X2RmJHN1YmNsYXNzZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJFcGVuZHltYWwiLCAiTlBDIiwgIkdsdXRhbWF0ZXJnaWMiKSkKICBjcGUgPSByb3VuZChjb3IocGxvdF9kZiRuZXdwdCwgcGxvdF9kZiRwc2V1ZG90aW1lXzQsIG1ldGhvZCA9ICJwZSIpLCAyKQogIAogIHBsdF9jb21wX2xbW25dXSA9IGdncGxvdChwbG90X2RmLCBhZXMoeCA9IG5ld3B0LCB5ID0gcHNldWRvdGltZV80LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IGNlbGxjbHVzdGVycywgc2l6ZSA9IHN1YmNsYXNzZXMpKSsKICAgIGdlb21fcG9pbnQoKSsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY29sc19jYywgbGltaXRzID0gZm9yY2UpKwogICAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYygwLjUsIDAuOTUsIDEuMzUpKSsKICAgIGxhYnModGl0bGUgPSBuLCBzdWJ0aXRsZSA9IHBhc3RlMCgiUENDID0gIiwgY3BlKSwgCiAgICAgICAgIHggPSAiUHNldWRvdGltZSAoc2NWZWxvKSIsIHkgPSAiUHNldWRvdGltZSAoVVJEKSIpKwogICAgdGhlbWVfY2xhc3NpYygpKwogICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gNiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjM1LCAiY20iKSkKfQpgYGAKClNhdmUgY29tcGFyaXNvbnMKCmBgYHtyfQpmb3IobiBpbiBuYW1lcyhwbHRfY29tcF9sKSl7CiAgcGRmKHBhc3RlMCgicmVzdWx0cy9wc2V1ZG90aW1lL1NTXyIsIG4sICJfY29tcC5wZGYiKSwgaGVpZ2h0ID0gNCwgd2lkdGggPSA0LjUpCiAgcHJpbnQocGx0X2NvbXBfbFtbbl1dKQogIGRldi5vZmYoKQp9CmBgYAoKTWFrZSBwcm9wb3J0aW9uIHBsb3RzCgpgYGB7cn0Kc21vX3Byb3BfbGlzdCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhzdWJmaXhfNF9sKSl7CiAgIyBzdWJzZXQgZGF0YQogIHN1Ym1ldGEgPSBkYXRhLmZyYW1lKCJwc2V1ZG90aW1lIiA9IHN1YmZpeF80X2xbW25dXUBwc2V1ZG90aW1lJHBzZXVkb3RpbWVfNCwKICAgICAgICAgICAgICAgICAgICAgICAiY2VsbGNsdXN0ZXJzIiA9IHN1YmZpeF80X2xbW25dXUBtZXRhJGNlbGxjbHVzdGVycykKICAKICBtZWRfcHRfY2MgPSBzb3J0KHRhcHBseShzdWJtZXRhJHBzZXVkb3RpbWUsIHN1Ym1ldGEkY2VsbGNsdXN0ZXJzLCBtZWRpYW4pKQogIAogIGx0X2JpbnMgPSBjdXQoc3VibWV0YSRwc2V1ZG90aW1lLCAxMDApICMgMTAwIGVxdWFsbHktc2l6ZWQgYmlucwogIHBsb3RfZGYgPSBkYXRhLmZyYW1lKGJpbnMgPSBsdF9iaW5zLCAKICAgICAgICAgICAgICAgICAgICAgICBjc3QgPSBhcy5jaGFyYWN0ZXIoc3VibWV0YSRjZWxsY2x1c3RlcnMpKQogIHRhYl9kZiA9IHRhYmxlKHBsb3RfZGYkYmlucywgcGxvdF9kZiRjc3QpCiAgCiAgIyByZW1vdmUgY2VsbCB0eXBlcyB0aGF0IGFyZSB0b28gcmFyZSAoPDUlKQogIHRhYl9kZiA9IHJlc2hhcGUyOjptZWx0KHRhYl9kZi9yb3dTdW1zKHRhYl9kZikpCiAgdXNlY2wgPSB0YXBwbHkodGFiX2RmJHZhbHVlLCB0YWJfZGYkVmFyMiwgZnVuY3Rpb24oeCkgYW55KHg+MC4wNSkpCiAgcGxvdF9kZiA9IHBsb3RfZGZbcGxvdF9kZiRjc3QgJWluJSBuYW1lcyh1c2VjbClbdXNlY2xdLF0KICB0YWJfZGYgPSB0YWJsZShwbG90X2RmJGJpbnMscGxvdF9kZiRjc3QpCiAgCiAgIyBub3JtYWxpc2UgYnkgY2VsbCB0eXBlIGFidW5kYW5jZQogIHByb3BfdyA9IHByb3AudGFibGUodGFibGUocGxvdF9kZiRjc3QpKQogIHRhYl9kZiA9IHQoYXBwbHkodGFiX2RmLCAxLCBmdW5jdGlvbih4KSB4L3Byb3Bfd1tjb2xuYW1lcyh0YWJfZGYpXSkpCiAgCiAgIyByZXNoYXBlCiAgdGFiX2RmID0gcmVzaGFwZTI6Om1lbHQodGFiX2RmL3Jvd1N1bXModGFiX2RmKSkKICB0YWJfZGYkVmFyMiA9IGFzLmNoYXJhY3Rlcih0YWJfZGYkVmFyMikKICAKICAjIHByZXZlbnQgZGlzY29udGludWl0eSBieSBjb3B5aW5nIHRoZSBwcmV2aW91cyBjb2x1bW4gKGxpa2VseSBub3QgaGFwcGVuaW5nKQogIHRhYl9kZiA9IHRhYl9kZltvcmRlcih0YWJfZGYkVmFyMSwgZGVjcmVhc2luZyA9IEYpLF0KICBmb3IoaSBpbiB1bmlxdWUodGFiX2RmJFZhcjEpKXsKICAgIGlmKGFueShpcy5uYW4odGFiX2RmJHZhbHVlW3RhYl9kZiRWYXIxPT1pXSkpKXsKICAgICAgdGFiX2RmJHZhbHVlW3RhYl9kZiRWYXIxPT1pXSA9IHByZXYKICAgIH0KICAgIHByZXYgPSB0YWJfZGYkdmFsdWVbdGFiX2RmJFZhcjE9PWldCiAgfQogIAogICMgc21vb3RoZW4gdGhlIHByb3BvcnRpb25zIChhbmQgZm9yY2UgY29uc3RyYWluIHRvIDAtMSkKICB0YWJfZGYyID0gdGFiX2RmCiAgdGFiX2RmMiR2YWx1ZTIgPSB0YWJfZGYyJHZhbHVlCiAgZm9yKGkgaW4gdW5pcXVlKHRhYl9kZjIkVmFyMikpewogICAgZmZmID0gbG9lc3ModmFsdWV+YXMubnVtZXJpYyhWYXIxKSwgZGF0YSA9IHRhYl9kZjJbdGFiX2RmMiRWYXIyPT1pLF0sIAogICAgICAgICAgICAgICAgc3BhbiA9IDAuNSkKICAgIHByZWQgPSBwcmVkaWN0KGZmZikKICAgIHByZWRbcHJlZD4xXSA9IDEKICAgIHByZWRbcHJlZDwwXSA9IDAKICAgIHRhYl9kZjIkdmFsdWUyW3RhYl9kZjIkVmFyMj09aV0gPSBwcmVkCiAgfQogIAogICMgZm9yY2UgY29uc3RyYWluIGVhY2ggaW50ZXJ2YWwgdG8gMC0xIGJ5IGRvaW5nIHByb3BvcnRpb24KICBmb3IoaSBpbiB1bmlxdWUodGFiX2RmMiRWYXIxKSl7CiAgICB0YWJfZGYyJHZhbHVlMlt0YWJfZGYyJFZhcjE9PWldID0gdGFiX2RmMiR2YWx1ZTJbdGFiX2RmMiRWYXIxPT1pXS9zdW0odGFiX2RmMiR2YWx1ZTJbdGFiX2RmMiRWYXIxPT1pXSkKICB9CiAgCiAgdGFiX2RmMiRtYWpvciA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQodGFiX2RmMiRWYXIyLCAiXyIpLCBmdW5jdGlvbih4KSB4WzFdKSkKICByZXMgPSBsaXN0KCkKICBmb3Iobm5uIGluIHVuaXF1ZSh0YWJfZGYyJFZhcjEpKXsKICAgIHNzPXRhcHBseSh0YWJfZGYyW3RhYl9kZjIkVmFyMT09bm5uLCJ2YWx1ZTIiXSwgdGFiX2RmMlt0YWJfZGYyJFZhcjE9PW5ubiwibWFqb3IiXSwgc3VtKQogICAgcmVzW1tubm5dXSA9IHdoaWNoLm1heChzcykKICB9CgogIHNtb19wcm9wX2xpc3RbW25dXSA9IGdncGxvdCh0YWJfZGYyLCBhZXMoeCA9IFZhcjEsIHkgPSB2YWx1ZTIsIGdyb3VwID0gVmFyMiwgZmlsbCA9IFZhcjIpKSsKICAgIGdlb21fYXJlYSgpKwogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkrCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xzX2NjW25hbWVzKGNvbHNfY2MpICVpbiUgdGFiX2RmMiRWYXIyXSkrCiAgICBsYWJzKHggPSAiQmlucyIsIHkgPSAiUHJvcG9ydGlvbiIsIGZpbGwgPSAiQ2VsbCB0eXBlIikrCiAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2LjUsIGNvbG91ciA9ICJibGFjayIpLAogICAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNyksCiAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjQsICJjbSIpKQp9CmBgYAoKU2F2ZSBwcm9wb3J0aW9ucwoKYGBge3J9CmZvcihuIGluIG5hbWVzKHNtb19wcm9wX2xpc3QpKXsKICBwZGYocGFzdGUwKCJyZXN1bHRzL3BzZXVkb3RpbWUvU1NfIiwgbiwgIl9wcm9wLnBkZiIpLCBoZWlnaHQgPSAzLCB3aWR0aCA9IDUuMikKICBwcmludChzbW9fcHJvcF9saXN0W1tuXV0pCiAgZGV2Lm9mZigpCn0KYGBgCgoKCgoKCgoKCgoKCgoKCg==